CloudFront caching in front of API Gateway is a powerful way to reduce latency and cost, but it’s not just about making things faster; it’s about fundamentally changing how your API Gateway is used.

Let’s see it in action. Imagine an API Gateway endpoint /users/{userId} that fetches user data. Without caching, every request hits API Gateway, which then invokes your backend Lambda function.

// Request 1: GET /users/123
{
  "requestContext": {
    "http": {
      "method": "GET",
      "path": "/users/123"
    }
  },
  "pathParameters": {
    "userId": "123"
  }
}

// Lambda response:
{
  "statusCode": 200,
  "body": "{\"userId\": \"123\", \"name\": \"Alice\"}"
}

// Request 2: GET /users/123 (same user)
// ... API Gateway invokes Lambda again ...

Now, let’s introduce CloudFront. We configure a CloudFront distribution with an origin pointing to our API Gateway endpoint.

Here’s a snippet of a CloudFront distribution configuration in CloudFormation:

Resources:
  MyCloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Origins:
          - DomainName: !GetAtt MyApiGateway.DomainName # Your API Gateway domain
            Id: api-gateway-origin
            CustomOriginConfig:
              HTTPPort: 80
              HTTPSPort: 443
              OriginProtocolPolicy: https-only
        DefaultCacheBehavior:
          TargetOriginId: api-gateway-origin
          ViewerProtocolPolicy: redirect-to-https
          AllowedMethods:
            - GET
            - HEAD
            - OPTIONS
          CachePolicyId: Managed-CachingOptimized # Or a custom cache policy
          Compress: true
        Enabled: true
        DefaultRootObject: ""
        HttpVersion: http2

With this setup, the first request for /users/123 still goes through API Gateway to Lambda. But CloudFront caches the response.

// Request 1: GET /users/123 (via CloudFront)
// CloudFront forwards to API Gateway -> Lambda
// Lambda returns: {"userId": "123", "name": "Alice"}
// CloudFront caches this response.

// Request 2: GET /users/123 (via CloudFront, within cache TTL)
// CloudFront checks its cache. Cache hit!
// CloudFront returns the cached response *directly* to the client.
// API Gateway and Lambda are NOT invoked.

The problem this solves is twofold: latency and cost. Every API Gateway request incurs a cost, and the round trip to your backend adds latency. By serving responses from CloudFront’s edge locations, you drastically cut both. Latency drops because the data is closer to the user, and cost drops because API Gateway and backend invocations are skipped.

Internally, CloudFront uses a cache key to determine if a response is cacheable and to retrieve it. This cache key is constructed based on various factors. For API Gateway, the most crucial elements are the HTTP method, the path, and query string parameters. For GET requests, you can typically cache responses for a set Time To Live (TTL). If the request method is not GET or HEAD, or if it contains parameters that vary per user (like an Authorization header), the default caching behavior might be to not cache, or to use a very short TTL.

The exact levers you control are within the CloudFront Cache Policy. You define what goes into the cache key (e.g., headers, cookies, query strings) and the TTL settings (Min, Max, Default TTL). A common mistake is to use a default "Managed-CachingOptimized" policy without understanding that it might cache based on headers like Authorization, which you don’t want to cache. For APIs, you often need a custom cache policy that excludes sensitive headers from the cache key but includes varying query parameters if they define the resource.

For example, a custom cache policy might look like this in CloudFormation:

Resources:
  MyCustomCachePolicy:
    Type: AWS::CloudFront::CachePolicy
    Properties:
      CachePolicyConfig:
        Name: api-gateway-user-cache
        Comment: Cache for user data, keyed by path and userId query param
        DefaultTTL: 300 # 5 minutes
        MaxTTL: 86400 # 1 day
        MinTTL: 60    # 1 minute
        ParametersInCacheKeyAndForwardedToOrigin:
          EnableAcceptEncodingGzip: true
          Headers:
            Quantity: 0 # No headers in cache key
          Cookies:
            Behavior: none # No cookies in cache key
          QueryStrings:
            AllowList: # Only include userId if it's part of the query string
              - userId
            Quantity: 1

When you need to invalidate the cache (e.g., after user data is updated), you create an invalidation in CloudFront. This tells CloudFront to remove specific objects from its edge caches, forcing it to fetch fresh data from the origin on the next request.

The next hurdle you’ll encounter is handling dynamic content or authentication within a cached response.

Want structured learning?

Take the full Apigateway course →