Implementing Dynamic CloudFront Rewrites With Lambda@Edge
CloudFront can serve more than cached files from an origin. With Lambda@Edge, a request can be inspected and rewritten before it reaches Amazon S3, an Application Load Balancer, or another HTTP endpoint. This is useful when a single distribution must support clean URLs, tenant-aware paths, legacy redirects, or different application entry points.
The pattern is particularly valuable in hybrid environments, where Australian organisations may host static content in Sydney while forwarding selected requests to workloads in Melbourne, Brisbane, or an on-premises data centre. A carefully designed edge rewrite reduces origin changes, improves cache behaviour, and gives operations teams a repeatable way to manage URL logic.
Choose The Right Edge Compute Option
CloudFront Functions are designed for lightweight viewer-request and viewer-response operations. They start quickly, cost less for high-volume transformations, and suit tasks such as normalising a path, adding a simple header, or redirecting a known URL pattern. They cannot make network calls and use a restricted JavaScript runtime.
Lambda@Edge runs standard Lambda code in a CloudFront-managed regional location. It supports Node.js and Python runtimes, has more memory and execution time, and is appropriate when rewrite logic needs richer parsing or more extensive application behaviour. A Lambda@Edge function can run on viewer request, viewer response, origin request, or origin response events.
For straightforward URL changes, use CloudFront Functions first. Choose Lambda@Edge when the rewrite depends on cookies, headers, complex path rules, or origin-request processing. An origin-request trigger can avoid running logic for cache hits, although viewer-request logic is often needed when the rewritten URI must influence the cache key.
Prepare The Distribution And Function
Create the Lambda function in the US East (N. Virginia) AWS Region. This requirement is easy to miss when the rest of an Australian deployment is in ap-southeast-2. Use a purpose-built IAM role with the trust relationship required by lambda.amazonaws.com and edgelambda.amazonaws.com, then publish a numbered version. CloudFront cannot associate the $LATEST version with a distribution.
A basic Node.js rewrite can map extensionless application routes to an SPA entry point while leaving assets unchanged:
'use strict';
exports.handler = async (event) => {
const request = event.Records[0].cf.request;
const uri = request.uri;
if (uri === '/' || (!uri.includes('.') && !uri.endsWith('/'))) {
request.uri = '/index.html';
}
return request;
};
This example is intentionally conservative. It avoids rewriting JavaScript, CSS, images, fonts, and API paths that contain a file extension. For a multi-tenant site, the function could instead transform /customers/acme/dashboard into /tenants/acme/dashboard.html, but validate the tenant identifier before constructing the new URI.
Attach The Rewrite To A Behaviour
Open the CloudFront distribution, select the relevant cache behaviour, and associate the published Lambda version with the viewer-request or origin-request event. Keep static assets and APIs in separate behaviours where possible. For example, /assets/* can use an S3 origin, /api/* can forward to an ALB, and the default behaviour can serve the web application shell.
The event choice affects both performance and correctness. A viewer-request rewrite occurs before CloudFront searches the cache, so /dashboard and /index.html can be made to share a cache object. An origin-request rewrite occurs after cache lookup, which can reduce Lambda invocation volume but may leave separate cache keys for equivalent URLs.
CloudFront deployments take time to propagate to edge locations. Test the distribution hostname rather than an origin hostname, and inspect response headers such as X-Cache, Age, and Via. In Australia, a tester in Perth may see a different edge location and cache state from one in Sydney, so allow for propagation and regional cache warming when validating a release.
Handle Caching, Headers, And Failures
A rewrite changes the URI CloudFront uses to locate content, but it does not automatically make every request safe to cache. Review cache policies, origin request policies, query strings, cookies, and headers that influence application output. If a tenant or locale is selected from a cookie, that value must be reflected in the cache key or handled outside a shared cache.
Avoid sending authentication-bearing requests through a broadly cached behaviour. APIs should generally use a dedicated policy with suitable forwarding and method settings. For an S3 website or REST origin, also check whether the rewritten path exists and whether the origin access control configuration permits CloudFront to retrieve it.
Logging is essential because Lambda@Edge execution logs are written in the AWS Region where the function runs, rather than being kept in one Australian log location. Include a compact request identifier and the original URI in diagnostic output, while avoiding tokens and personal data. CloudFront standard logs or real-time logs can then be correlated with origin logs and application monitoring.
Test And Operate The Deployment
Test known routes, missing routes, encoded characters, trailing slashes, query strings, HEAD requests, and paths containing dots. Confirm that /dashboard reaches the intended document, /assets/app.js remains unchanged, and /api/health is not accidentally rewritten. Also verify that an origin error does not become a cached error for longer than intended.
For teams supporting mobile and web delivery, edge routing often sits alongside frontend release automation. Lessons from Swift challenge projects can be useful here: keep the deployment contract explicit, make environments reproducible, and test the user journey rather than only the build artifact. The same discipline applies when a CloudFront distribution fronts an Australian customer portal or a home-lab service published through a controlled endpoint.
Use versioned Lambda functions and infrastructure as code so that distribution associations can be reviewed and rolled back. A change to a function creates a new immutable version; updating the association to that version provides a clear deployment boundary. Keep the previous version available until propagation and smoke tests are complete, then remove obsolete versions according to your operational policy.
Track Lambda@Edge errors, CloudFront 4xx and 5xx responses, origin latency, cache-hit ratio, and unexpected invocation growth. This makes it easier to distinguish a bad rewrite from an S3 permission issue, an ALB health problem, or a cache-policy mistake. With these controls in place, dynamic URL handling remains close to the user while the origin architecture stays simpler and easier to maintain.