Introduction
By day 36 of building on AWS, you've likely deployed applications and watched real users hit your servers from across the globe—only to discover that latency is eroding your user experience. CloudFront essentials form the foundation of how we serve content faster to every corner of the world, and understanding its mechanics is critical for any developer shipping production systems. In this guide, we'll walk through the practical decisions you'll make: choosing origins, defining cache behaviors, securing at the edge, and running logic on CloudFront Functions.
Origins: where your content lives
An origin is simply the source of truth for your content. CloudFront doesn't store your data; it caches what it fetches from an origin and serves it from edge locations worldwide. The origin can be almost anything with an HTTP endpoint, but in practice, we're selecting from a few key options.
S3 buckets are the most common origin for static assets. When you point CloudFront to an S3 bucket, you're not exposing the bucket publicly; instead, CloudFront uses an Origin Access Identity (OAI) or Origin Access Control (OAC) to authenticate and fetch objects. This keeps your bucket private and lets CloudFront handle all the distribution.
Application Load Balancers (ALB) work well when you're running dynamic content in EC2 or ECS. CloudFront will cache responses based on headers and query strings, forwarding requests to your ALB when cache misses occur. This is especially useful for API endpoints or personalized pages where you want the CDN to absorb repeated requests for the same resource.
API Gateway origins make sense for serverless architectures. You can put CloudFront in front of your REST or HTTP API, gaining the cache layer and edge security benefits without managing infrastructure.
Custom HTTP servers anywhere on the internet can be origins too. You might have a legacy system or third-party service; as long as it responds to HTTP requests, CloudFront can cache from it.
In real systems, we often use multiple origins in the same distribution. An e-commerce platform might serve static assets (images, CSS, JavaScript) from S3, product APIs from API Gateway, and user accounts from an ALB. Each origin gets its own configuration, and we decide which requests route to which origin via behaviors.
Behaviors: routing and caching rules per path
A behavior is a rule that says "for requests matching this path pattern, apply this set of cache and forwarding rules." Every CloudFront distribution starts with a default behavior that catches all requests; then you layer specific behaviors on top to override that default for particular paths.
Consider a SaaS application serving:
- /static/* — Images, stylesheets, JavaScript (cache for 1 year)
- /api/* — Dynamic API responses (cache for 1 minute, cache by Authorization header)
- /user-profile — Personalized page (don't cache, forward all headers)
- /videos/* — Large media (cache for 24 hours from a video streaming origin)
Each path pattern gets its own behavior. Within a behavior, you specify:
- Origin — Which backend handles this request.
- Viewer protocol policy — Redirect HTTP to HTTPS, or allow both.
- Allowed methods — GET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS.
- Cache key — What distinguishes one cached response from another (path, query string, headers, cookies).
- TTL — How long to cache before revalidating with the origin.
The cache key is critical. By default, CloudFront caches based on the request path and hostname. If you include query strings in the cache key, then ?sort=name and ?sort=date are cached separately. If you include cookies, then requests with different session IDs won't share the cache. For static assets, you want a simple cache key; for APIs, you might cache by Authorization header to serve each user's data independently.
Here's a common pitfall: forgetting to whitelist headers and query strings means CloudFront ignores them when caching, so requests that should be different get served the same response. We recommend being explicit: list exactly which headers, query strings, and cookies matter for caching.
Edge security: TLS, WAF, and geo restrictions
CloudFront terminates TLS at the edge, closer to your users, before traffic travels back to your origin. This reduces latency for the TLS handshake and offloads encryption work from your servers.
You associate a CloudFront distribution with a certificate issued by AWS Certificate Manager (ACM). The certificate can be a wildcard (*.example.com) or a Subject Alternative Name (SAN) covering multiple domains. CloudFront automatically handles certificate renewal.
For the connection from CloudFront edge locations to your origin, you can choose HTTP or HTTPS. We recommend HTTPS to secure origin traffic, especially if your origin is exposed to the public internet. You can also restrict traffic to your origin by configuring a security group or S3 bucket policy that only accepts requests from CloudFront.
AWS WAF integrates tightly with CloudFront. You attach a WAF ACL to a distribution and enforce rules like rate limiting, IP blocking, geo-blocking, SQL injection detection, and bot control—all at edge locations before traffic reaches your origin. This is far more cost-effective than filtering traffic at your application layer.
Geo-blocking is a CloudFront feature (distinct from WAF) that lets you allow or deny access based on the viewer's country. If you're serving content only in specific regions for licensing or compliance, geo-blocking prevents unwanted requests from reaching your origin and wasting bandwidth.
A real example: a financial services firm deployed a CloudFront distribution with a WAF rule that blocked requests with unusual query strings (SQL injection attempts), rate-limited to 2,000 requests per 5 minutes per IP, and geo-blocked access to sanctioned countries. WAF also had a managed rule for common vulnerabilities. The result was negligible origin traffic from malicious requests—the edge absorbed and rejected them.
Functions at the edge: CloudFront Functions for redirects and headers
CloudFront Functions let you run lightweight JavaScript at edge locations, modifying requests and responses before they reach your origin or viewer. They're different from Lambda@Edge (which we'll cover later); CloudFront Functions are simpler, faster, and cheaper for common tasks like redirects, header manipulation, and basic URL rewriting.
A CloudFront Function has access to the request object and can read and modify:
- Headers
- Query strings
- Cookies
- URI and domain
Common use cases:
- Add security headers — Inject Content-Security-Policy, X-Frame-Options, or Strict-Transport-Security.
- Redirect old URLs — Rewrite /old-path to /new-path without hitting your origin.
- Route by device type — Check User-Agent and redirect mobile traffic to a subdomain.
- Authentication checks — Verify a cookie or token before allowing the request through.
Here's a simple example that adds a security header and redirects old URLs:
function handler(event) {
var request = event.request;
// Redirect legacy URLs
if (request.uri === '/old-blog-post') {
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: {
location: { value: '/blog/new-post' }
}
};
}
// Add security header
request.headers['strict-transport-security'] = {
value: 'max-age=31536000; includeSubDomains'
};
return request;
}
CloudFront Functions execute in milliseconds, making them ideal for frequent operations. They're billed per execution, which means high-traffic sites can save money compared to Lambda@Edge, which has higher latency and per-duration pricing.
Common mistakes
- Wrong cache key configuration — Forwarding all headers and query strings to the origin but not including them in the cache key, causing CloudFront to serve stale or incorrect content. Always audit your cache key settings.
- Not using Origin Access Control — Leaving S3 buckets public when CloudFront could authenticate privately. This exposes you to unexpected bandwidth bills and direct S3 access.
- Overly aggressive TTLs — Setting cache TTL too high on frequently updated content leads to users seeing stale data for hours. Be conservative with TTLs for dynamic content.
- Missing HTTPS from edge to origin — Using HTTP between CloudFront and your origin leaks sensitive data in transit and violates compliance requirements in most organizations.
- Not testing behavior precedence — Behaviors are evaluated top-to-bottom; a too-broad pattern can override a more specific one. Test your routing in staging.
Key takeaways
- Choose origins based on your workload: S3 for static assets, ALB for dynamic content, API Gateway for serverless APIs.
- Behaviors let you apply different cache and routing rules per path pattern; be explicit about cache keys to avoid serving wrong content.
- Secure at the edge with HTTPS, AWS WAF for application-layer threats, and geo-blocking for regional restrictions.
- Use CloudFront Functions for lightweight request/response manipulation—redirects, headers, authentication checks—without the latency and cost of Lambda@Edge.
- Always use Origin Access Control for S3, validate your cache key settings in staging, and start conservative with TTLs on dynamic content.






Comments
Loading comments…