How to Integrate AWS CloudFront with API Gateway for GET/POST Routing on a Single Domain


2 views

When building modern web applications on AWS, we often need to combine static content delivery with dynamic API processing under the same domain. Here's how to configure CloudFront and API Gateway to work together:

First, set up your CloudFront distribution pointing to your S3 bucket for static assets:

Resources:
  MyDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Enabled: true
        DefaultRootObject: index.html
        Origins:
          - DomainName: my-bucket.s3.amazonaws.com
            Id: S3Origin
            S3OriginConfig: {}
        DefaultCacheBehavior:
          TargetOriginId: S3Origin
          ViewerProtocolPolicy: redirect-to-https
          AllowedMethods:
            - GET
            - HEAD
          CachedMethods:
            - GET
            - HEAD

Create a new API Gateway REST API with Lambda integration for POST requests:

Resources:
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: prod
      DefinitionBody:
        swagger: "2.0"
        paths:
          /api/submit:
            post:
              x-amazon-apigateway-integration:
                uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${SubmitFunction.Arn}/invocations
                httpMethod: POST
                type: aws_proxy

Add a custom behavior in CloudFront to route specific paths to API Gateway:

Resources:
  MyDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        # ... existing config ...
        CacheBehaviors:
          - PathPattern: /api/*
            TargetOriginId: ApiGatewayOrigin
            AllowedMethods:
              - GET
              - POST
              - PUT
              - DELETE
              - HEAD
              - OPTIONS
            ForwardedValues:
              QueryString: true
              Cookies:
                Forward: all
            ViewerProtocolPolicy: redirect-to-https

Set up your API Gateway as a custom origin in CloudFront:

Origins:
  - DomainName: !Sub ${MyApi}.execute-api.${AWS::Region}.amazonaws.com
    Id: ApiGatewayOrigin
    CustomOriginConfig:
      HTTPPort: 80
      HTTPSPort: 443
      OriginProtocolPolicy: https-only

Configure appropriate cache settings for your API paths:

CacheBehaviors:
  - PathPattern: /api/submit
    TargetOriginId: ApiGatewayOrigin
    DefaultTTL: 0
    MinTTL: 0
    MaxTTL: 0

Implement proper security measures for your API endpoints:

Resources:
  ApiPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref SubmitFunction
      Principal: apigateway.amazonaws.com
      SourceArn: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${MyApi}/*/*/*

Verify your configuration works correctly:

# Test static content
curl https://example.com/index.html

# Test API endpoint
curl -X POST https://example.com/api/submit -d '{"test":"data"}'

Configure custom error responses in CloudFront:

ErrorPages:
  - ErrorCode: 404
    ResponseCode: 200
    ResponsePagePath: /index.html

When building modern web applications, we often need to serve both static content (HTML, CSS, JS) and handle dynamic API requests through the same domain. AWS provides excellent services for each component:

  • CloudFront + S3 for static asset delivery
  • API Gateway + Lambda for dynamic backend processing

The core problem emerges when we need to:


static.example.com  → CloudFront → S3 (GET requests)
static.example.com  → API Gateway → Lambda (POST requests)

We can't simply point the domain to both services simultaneously using DNS. Here's the proper approach.

The most elegant solution is to use CloudFront as the single entry point that routes requests based on:

  • HTTP method (GET vs POST)
  • Path patterns (/api/* vs /*)
  • Headers or other request characteristics

Here's how to configure this in AWS:

1. Create API Gateway Endpoint

First, set up your API Gateway with Lambda integration:


// Example Lambda function for API Gateway
exports.handler = async (event) => {
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

2. Configure CloudFront Distribution

Create a CloudFront distribution with:

  • S3 origin for static content
  • Custom origin pointing to your API Gateway endpoint

3. Set Up Cache Behaviors

Configure path-based routing in CloudFront:


Behaviors:
- Path Pattern: /api/*
  Origin: API Gateway
  Allowed HTTP Methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
  Cache Policy: CachingDisabled

- Path Pattern: /*
  Origin: S3 Bucket
  Allowed HTTP Methods: GET, HEAD
  Cache Policy: Managed-CachingOptimized

For more complex scenarios:

Lambda@Edge for Dynamic Routing

You can use Lambda@Edge to make routing decisions:


exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    
    // Route POST requests to API Gateway
    if (request.method === 'POST') {
        request.origin = {
            custom: {
                domainName: 'your-api-id.execute-api.region.amazonaws.com',
                port: 443,
                protocol: 'https',
                path: '',
                sslProtocols: ['TLSv1.2'],
            }
        };
    }
    
    callback(null, request);
};

Handling CORS

Ensure proper CORS configuration for API Gateway:


# CloudFormation snippet for CORS
ApiGatewayMethod:
  Type: AWS::ApiGateway::Method
  Properties:
    HttpMethod: OPTIONS
    ResourceId: !Ref ApiGatewayResource
    RestApiId: !Ref ApiGateway
    Integration:
      Type: MOCK
      RequestTemplates:
        application/json: '{"statusCode": 200}'

When implementing this architecture:

  • Use regional API endpoints for lower latency
  • Configure CloudFront TTLs appropriately for static content
  • Enable compression in both CloudFront and API Gateway
  • Monitor cache hit ratios in CloudFront

Some frequent challenges include:

  • 403 errors from API Gateway - check IAM permissions
  • Mixed content warnings - ensure all origins use HTTPS
  • Cache invalidation delays - use versioned paths for static assets