Skip to main content

Command Palette

Search for a command to run...

CloudFront Distribution: Global CDN

Published
8 min read
T

Welcome to TopperBlog! 👋

I'm a tech content creator passionate about helping developers level up their careers and master cutting-edge technologies.

🎯 What I Write About: • AI/ML Engineering & LLMs • Web3 & Blockchain Development
• System Design & Architecture • Interview Preparation (FAANG) • Freelancing & Remote Work • Modern Tech Stacks (Next.js, React, Rust, TypeScript) • Performance Optimization & Best Practices

💼 Mission: Sharing practical, actionable insights that accelerate your tech career and maximize your earning potential.

📚 15+ In-Depth Guides covering everything from earning $10k/month as a freelancer to cracking FAANG interviews.

🌐 Let's connect and grow together in this amazing tech journey!

#TechBlogger #SoftwareEngineering #CareerGrowth #WebDevelopment #AIEngineering

CloudFront Distribution: Global CDN Setup

Modern applications serve users across continents with millisecond-level latency expectations, yet many engineering teams still treat content delivery as an afterthought. A poorly configured CloudFront distribution setup doesn't just slow down your application—it creates cascading failures during traffic spikes, exposes security vulnerabilities through misconfigured origins, and generates unnecessary AWS bills that can reach thousands of dollars monthly. In 2025, with AI-generated content, real-time personalization, and privacy regulations like GDPR and CCPA requiring regional data handling, your CDN architecture must be both globally distributed and intelligently segmented.

The stakes are higher than ever. A single misconfigured cache behavior can expose authenticated API responses to anonymous users. Incorrect origin failover settings mean your entire application goes dark when a single region experiences issues. Without proper edge computing integration, you're forcing expensive origin requests for operations that could execute at the edge in under 10ms. These aren't theoretical concerns—they're production incidents that cost companies revenue, customer trust, and engineering hours every day.

Why Traditional CDN Approaches Fail Modern Requirements

Five years ago, setting up a CDN meant pointing it at your origin server and configuring basic cache headers. That approach breaks down immediately in contemporary architectures. Modern applications require dynamic content personalization based on user location, device type, and authentication state. They need to comply with data residency requirements that prevent certain content from being cached in specific geographic regions. They must handle WebSocket connections, HTTP/3, and gRPC traffic alongside traditional HTTP requests.

Traditional CDN configurations also fail to account for the explosion in edge computing capabilities. CloudFront Functions and Lambda@Edge now enable request/response manipulation, A/B testing, authentication, and even lightweight API logic at edge locations. Teams that treat CloudFront as a simple caching layer miss opportunities to reduce origin load by 80% or more while simultaneously improving response times.

The cost implications are equally significant. Without intelligent cache key normalization, you might be caching thousands of variations of identical content, wasting storage and reducing cache hit ratios. Incorrect origin request settings can generate millions of unnecessary requests to your application servers, driving up both CloudFront and compute costs.

Architecting a Production-Grade CloudFront Distribution

A modern CloudFront distribution setup requires careful consideration of multiple interconnected components: origin configuration with failover, cache behaviors with intelligent key normalization, security policies including WAF integration, and edge computing functions for request manipulation.

Origin Configuration with High Availability

Your origin configuration determines reliability. A single origin point creates a single point of failure. Production architectures require origin groups with automatic failover:

import * as cdk from 'aws-cdk-lib';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
import * as s3 from 'aws-cdk-lib/aws-s3';

export class ProductionCDNStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Primary and failover origins
    const primaryBucket = s3.Bucket.fromBucketName(
      this,
      'PrimaryOrigin',
      'primary-content-us-east-1'
    );

    const failoverBucket = s3.Bucket.fromBucketName(
      this,
      'FailoverOrigin',
      'failover-content-eu-west-1'
    );

    // Origin Access Control for secure S3 access
    const oac = new cloudfront.S3OriginAccessControl(this, 'OAC', {
      signing: cloudfront.Signing.SIGV4_NO_OVERRIDE,
    });

    const originGroup = new origins.OriginGroup({
      primaryOrigin: new origins.S3Origin(primaryBucket, {
        originAccessControl: oac,
        connectionAttempts: 3,
        connectionTimeout: cdk.Duration.seconds(10),
      }),
      fallbackOrigin: new origins.S3Origin(failoverBucket, {
        originAccessControl: oac,
        connectionAttempts: 3,
        connectionTimeout: cdk.Duration.seconds(10),
      }),
      fallbackStatusCodes: [500, 502, 503, 504, 404, 403],
    });

    // Response headers policy for security
    const securityHeadersPolicy = new cloudfront.ResponseHeadersPolicy(
      this,
      'SecurityHeaders',
      {
        securityHeadersBehavior: {
          contentTypeOptions: { override: true },
          frameOptions: {
            frameOption: cloudfront.HeadersFrameOption.DENY,
            override: true,
          },
          referrerPolicy: {
            referrerPolicy: cloudfront.HeadersReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
            override: true,
          },
          strictTransportSecurity: {
            accessControlMaxAge: cdk.Duration.days(365),
            includeSubdomains: true,
            preload: true,
            override: true,
          },
          xssProtection: {
            protection: true,
            modeBlock: true,
            override: true,
          },
        },
        customHeadersBehavior: {
          customHeaders: [
            {
              header: 'Cache-Control',
              value: 'public, max-age=31536000, immutable',
              override: false,
            },
          ],
        },
      }
    );

    // Cache policy with intelligent key normalization
    const optimizedCachePolicy = new cloudfront.CachePolicy(
      this,
      'OptimizedCache',
      {
        cachePolicyName: 'OptimizedCachePolicy2025',
        comment: 'Optimized caching with normalized keys',
        defaultTtl: cdk.Duration.hours(24),
        maxTtl: cdk.Duration.days(365),
        minTtl: cdk.Duration.seconds(1),
        headerBehavior: cloudfront.CacheHeaderBehavior.allowList(
          'CloudFront-Viewer-Country',
          'CloudFront-Is-Mobile-Viewer'
        ),
        queryStringBehavior: cloudfront.CacheQueryStringBehavior.allowList(
          'version',
          'locale'
        ),
        cookieBehavior: cloudfront.CacheCookieBehavior.none(),
        enableAcceptEncodingGzip: true,
        enableAcceptEncodingBrotli: true,
      }
    );

    const distribution = new cloudfront.Distribution(this, 'CDN', {
      defaultBehavior: {
        origin: originGroup,
        viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
        allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
        cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS,
        cachePolicy: optimizedCachePolicy,
        responseHeadersPolicy: securityHeadersPolicy,
        compress: true,
      },
      priceClass: cloudfront.PriceClass.PRICE_CLASS_100,
      httpVersion: cloudfront.HttpVersion.HTTP3,
      enableIpv6: true,
      enableLogging: true,
      logBucket: s3.Bucket.fromBucketName(this, 'LogBucket', 'cdn-logs-bucket'),
      logFilePrefix: 'cloudfront/',
      logIncludesCookies: false,
    });

    new cdk.CfnOutput(this, 'DistributionDomainName', {
      value: distribution.distributionDomainName,
    });
  }
}

This configuration implements several critical production requirements. Origin Access Control (OAC) replaces the deprecated Origin Access Identity (OAI) and provides better security through SigV4 signing. The origin group ensures automatic failover when the primary origin returns error status codes. Connection timeouts and retry attempts are tuned for real-world network conditions.

Edge Computing for Request Manipulation

CloudFront Functions execute in under 1ms and cost significantly less than Lambda@Edge for simple transformations. Use them for URL rewrites, header manipulation, and request validation:

// CloudFront Function for intelligent routing and normalization
const edgeFunction = `
function handler(event) {
    var request = event.request;
    var uri = request.uri;
    var headers = request.headers;

    // Normalize trailing slashes
    if (uri.endsWith('/') && uri.length > 1) {
        request.uri = uri.slice(0, -1);
    }

    // Add index.html for directory requests
    if (uri.endsWith('/') || !uri.includes('.')) {
        request.uri = uri + (uri.endsWith('/') ? 'index.html' : '/index.html');
    }

    // Device-based routing
    var isMobile = headers['cloudfront-is-mobile-viewer'] 
        && headers['cloudfront-is-mobile-viewer'].value === 'true';

    if (isMobile && !uri.startsWith('/mobile/')) {
        request.uri = '/mobile' + request.uri;
    }

    // Geographic content routing
    var country = headers['cloudfront-viewer-country'] 
        ? headers['cloudfront-viewer-country'].value 
        : 'US';

    // GDPR compliance: prevent caching for EU users on sensitive paths
    if (['DE', 'FR', 'IT', 'ES', 'GB'].includes(country) 
        && uri.startsWith('/api/user')) {
        headers['cache-control'] = { value: 'no-store, private' };
    }

    // Query string normalization for better cache hit ratio
    if (request.querystring) {
        var params = new URLSearchParams(request.querystring);
        var normalized = new URLSearchParams();

        // Only keep meaningful parameters
        ['version', 'locale', 'format'].forEach(key => {
            if (params.has(key)) {
                normalized.set(key, params.get(key));
            }
        });

        request.querystring = normalized.toString();
    }

    return request;
}
`;

// In your CDK stack
const cfFunction = new cloudfront.Function(this, 'EdgeFunction', {
  code: cloudfront.FunctionCode.fromInline(edgeFunction),
  runtime: cloudfront.FunctionRuntime.JS_2_0,
});

// Attach to distribution behavior
const distribution = new cloudfront.Distribution(this, 'CDN', {
  defaultBehavior: {
    origin: originGroup,
    functionAssociations: [
      {
        function: cfFunction,
        eventType: cloudfront.FunctionEventType.VIEWER_REQUEST,
      },
    ],
    // ... other configuration
  },
});

This edge function handles multiple production scenarios: URL normalization improves cache hit ratios by eliminating variations, device-based routing serves optimized content, geographic routing ensures compliance with data residency requirements, and query string normalization prevents cache fragmentation.

Multi-Behavior Configuration for Complex Applications

Modern applications require different caching strategies for different content types. API responses need short TTLs and authentication, while static assets benefit from long-term caching:

// API behavior with no caching for authenticated requests
const apiCachePolicy = new cloudfront.CachePolicy(this, 'APICache', {
  cachePolicyName: 'APINoCache',
  defaultTtl: cdk.Duration.seconds(0),
  maxTtl: cdk.Duration.seconds(1),
  minTtl: cdk.Duration.seconds(0),
  headerBehavior: cloudfront.CacheHeaderBehavior.allowList(
    'Authorization',
    'CloudFront-Viewer-Country'
  ),
  queryStringBehavior: cloudfront.CacheQueryStringBehavior.all(),
  cookieBehavior: cloudfront.CacheCookieBehavior.all(),
});

const apiOrigin = new origins.HttpOrigin('api.example.com', {
  protocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY,
  httpsPort: 443,
  originSslProtocols: [cloudfront.OriginSslPolicy.TLS_V1_2],
  readTimeout: cdk.Duration.seconds(30),
  keepaliveTimeout: cdk.Duration.seconds(5),
  customHeaders: {
    'X-Origin-Verify': 'secret-token-here',
  },
});

distribution.addBehavior('/api/*', apiOrigin, {
  viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.HTTPS_ONLY,
  allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
  cachePolicy: apiCachePolicy,
  originRequestPolicy: cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
});

// Static assets with aggressive caching
const staticCachePolicy = new cloudfront.CachePolicy(this, 'StaticCache', {
  cachePolicyName: 'StaticAssetsCache',
  defaultTtl: cdk.Duration.days(365),
  maxTtl: cdk.Duration.days(365),
  minTtl: cdk.Duration.days(365),
  headerBehavior: cloudfront.CacheHeaderBehavior.none(),
  queryStringBehavior: cloudfront.CacheQueryStringBehavior.none(),
  cookieBehavior: cloudfront.CacheCookieBehavior.none(),
});

distribution.addBehavior('/static/*', originGroup, {
  viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.HTTPS_ONLY,
  allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD,
  cachePolicy: staticCachePolicy,
  compress: true,
});

Common Pitfalls and Edge Cases

Cache key explosion occurs when you include too many variables in your cache key. Every unique combination creates a separate cached object. A distribution caching based on all query parameters, cookies, and headers might generate millions of cache entries for identical content, destroying your cache hit ratio. Monitor your cache statistics and normalize keys aggressively.

Origin shield misconfiguration can increase costs rather than reduce them. Origin Shield adds an additional caching layer between edge locations and your origin, but it only helps when you have many edge locations requesting the same content. For low-traffic distributions or content with high geographic concentration, Origin Shield adds latency and cost without benefit.

Incorrect failover status codes cause unnecessary failover triggers. Including 404 in your failover status codes means CloudFront will try the failover origin for legitimately missing content, doubling your origin requests. Only include server error codes (5xx) unless you have specific requirements.

Missing WAF integration exposes your origin to attacks. CloudFront sits at the edge, making it the perfect place to filter malicious traffic before it reaches your infrastructure. Integrate AWS WAF with rate limiting, geographic restrictions, and bot detection:

import * as wafv2 from 'aws-cdk-lib/aws-wafv2';

const webAcl = new wafv2.CfnWebACL(this, 'CDNWebACL', {
  scope: 'CLOUDFRONT',
  defaultAction: { allow: {} },
  rules: [
    {
      name: 'RateLimitRule',
      priority: 1,
      statement: {
        rateBasedStatement: {
          limit: 2000,
          aggregateKeyType: 'IP',
        },
      },
      action: { block: {} },
      visibilityConfig: {
        sampledRequestsEnabled: true,
        cloudWatchMetricsEnabled: true,
        metricName: 'RateLimitRule',
      },
    },
    {
      name: 'GeoBlockRule',
      priority: 2,
      statement: {
        geoMatchStatement: {
          countryCodes: ['KP', 'IR', 'SY'],
        },
      },
      action: { block: {} },
      visibilityConfig: {
        sampledRequestsEnabled: true,
        cloudWatchMetricsEnabled: true,
        metricName: 'GeoBlockRule',
      },
    },
  ],
  visibilityConfig: {
    sampledRequestsEnabled: true,
    cloudWatchMetricsEnabled: true,
    metricName: 'CDNWebACL',
  },
});

// Associate with distribution
const distribution = new cloudfront.Distribution(this, 'CDN', {
  webAclId: webAcl.attrArn,
  // ... other configuration
});

Certificate validation failures happen when you don't properly configure custom domains. CloudFront requires certificates in us-east-1 regardless of your distribution's edge locations. Automate certificate validation through DNS:

import * as acm from 'aws-cdk-lib/aws-certificatemanager';
import * as route53 from 'aws-cdk-lib/aws-route53';

const hostedZone = route53.HostedZone.fromLookup(this, 'Zone', {
  domainName: 'example.com',
});

const certificate = new acm.Certificate(this, 'Certificate', {
  domainName: 'cdn.example.com',
  subjectAlternativeNames: ['*.cdn.example.com'],
  validation: acm.CertificateValidation.fromDns(hostedZone),
});

const distribution = new cloudfront.Distribution(this, 'CDN', {
  domainNames: ['cdn.example.com'],
  certificate: certificate,
  // ... other configuration
});

Best Practices for Production CloudFront Deployments

Implement comprehensive monitoring beyond basic CloudFront metrics. Track cache hit ratio, origin latency, 4xx/5xx error rates, and bytes downloaded. Set up CloudWatch alarms for cache hit ratios below 80%, error rates above 1%, and origin latency spikes:

import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';
import * as actions from 'aws-cdk-lib/aws-cloudwatch-actions';
import * as sns from 'aws-cdk-lib/aws-sns';

const alarmTopic = new sns.Topic(this, 'CDNAlarms');

new cloudwatch.Alarm(this, 'LowCacheHitRatio', {
  metric: new cloudwatch.Metric({
    namespace: 'AWS/CloudFront',
    metricName: 'CacheHitRate',
    dimensionsMap: {
      DistributionId: distribution.distributionId,
    },
    statistic: 'Average',
    period: cdk.Duration.minutes(5),
  }),
  threshold: 80,
  comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_THRESHOLD,
  evaluationPeriods: 2,
  actionsEnabled: true,
}).addAlarmAction(new actions.SnsAction(alarmTopic));

Use cache invalidation sparingly. Each invalidation costs money and doesn't take effect immediately. Instead, implement versioned URLs for static assets (/static/v2/app.js) and use short TTLs for frequently changing content. When you must invalidate, use wildcard patterns efficiently.

Enable real-time logs for debugging but route them to Kinesis Data Streams rather than S3 for cost efficiency. Real-time logs provide request-level visibility for troubleshooting cache misses, security incidents, and performance issues:

```typescript import * as kinesis from