skip to content

Edge Security & Edge Functions

Controlling who gets a cached object and running your own code at the edge: signed URLs and cookies, geo restriction, WAF and Shield in front, and CloudFront Functions versus Lambda@Edge. Which edge runtime fits which job is a frequent follow-up.

part ofAWSoverview, primer and where to startread it →
on this pageshow

questions

6

CloudFront supports both CloudFront Functions and Lambda@Edge. Which trigger points can each hook into, and how would you choose between them for a given piece of edge logic?

level: middleimportance: must knowfreq 66%

answer

  1. four hooks, one runtime sees only two
  2. viewer triggers fire on cache hits too
  3. no network calls in the cheap runtime
  4. origin selection needs the bigger runtime
  5. published version, authored in us-east-1

basics

~20 s

CloudFront Functions run only on viewer request and viewer response, in a tiny sandboxed JavaScript runtime with no network access — ideal for header and URI rewrites. Lambda@Edge also hooks origin request and origin response, and can call other services.

solid answer

~50 s

CloudFront exposes four trigger points: viewer request, origin request, origin response, viewer response. **CloudFront Functions** attach to the two viewer triggers only. They run a purpose-built JavaScript sandbox with a sub-millisecond execution budget, a very small code and memory allowance, no network or filesystem access, and no access to the request body — so they are for URI rewrites, redirects, header manipulation, cache-key normalization and simple token checks, at a fraction of the price of Lambda@Edge. **Lambda@Edge** attaches to all four triggers, runs Node.js or Python with the AWS SDK available, can make network calls, and gets a much larger time and memory budget on the origin-facing triggers. Anything that must talk to a database, mutate the origin selection, or inspect a request body needs Lambda@Edge. One cost fact drives design: viewer triggers fire on every single request, including cache hits, while origin triggers fire only on a cache miss.

code

javascript · 8 lines
javascript
function handler(event) {
  var request = event.request;
  var uri = request.uri;
  if (uri.charAt(uri.length - 1) === '/') {
    request.uri = uri + 'index.html';
  }
  return request;
}

go deeper

for a junior

Be able to name the two edge runtimes and say plainly that CloudFront Functions are for lightweight header and URL rewrites on viewer triggers, while Lambda@Edge is fuller Lambda that can also run on the origin side.

for a middle

Explain the four trigger points and which runtime attaches to which, plus the concrete limits: no network or body access and a sub-millisecond budget for Functions, versus SDK access and much larger time and memory budgets for Lambda@Edge.

for a senior

Demonstrate the cost and latency reasoning — viewer triggers fire on cache hits, origin triggers only on misses — and the operational realities: replicated logs in the serving Region, version-pinned associations, and the risk of putting a dependency call on the hot path.

for a principal

Own where edge compute belongs at all. Argue when logic should live at the edge versus in the origin service or the API layer, set the guardrails for who may deploy edge code, and weigh the rollback and blast-radius cost of globally replicated functions.

## The four trigger points A request passing through CloudFront crosses four hook points. **Viewer request** fires when CloudFront receives the request, before the cache is consulted. **Origin request** fires only when the cache misses and CloudFront is about to contact the origin. **Origin response** fires when the origin's reply arrives, before it is cached. **Viewer response** fires just before the reply is returned, whether it came from the origin or from the cache. That asymmetry is the single most useful fact here: viewer triggers run on *every* request; origin triggers run only on cache misses. A per-request cost or latency belongs on a viewer trigger only if you accept paying it on cache hits too, and expensive work that only affects what gets stored belongs on an origin trigger. ## CloudFront Functions CloudFront Functions run in a JavaScript engine built for the edge rather than a general-purpose runtime. They execute at the edge locations themselves and are designed for sub-millisecond execution — the quota is on the order of a millisecond, and exceeding it fails the request rather than slowing it. The sandbox is deliberately austere: a small maximum function size (on the order of kilobytes), a couple of megabytes of memory, **no network access, no filesystem, no access to the request or response body**, and no AWS SDK. The handler signature is `function handler(event)` and it returns either a modified request or a synthesized response. Deployment has two stages, `DEVELOPMENT` and `LIVE`, and a built-in test harness that replays a sample event. For lookup data there is CloudFront KeyValueStore, a read-only key-value store the function can consult without a network call. Typical jobs: append `index.html` to directory-style URIs, normalize or strip query strings so more requests share a cache entry, redirect on `Host` or path, inject security headers on the viewer response, do a cheap presence/format check on an authorization header before the request is allowed further. ```javascript function handler(event) { var request = event.request; var uri = request.uri; if (uri.charAt(uri.length - 1) === '/') { request.uri = uri + 'index.html'; } return request; } ``` ## Lambda@Edge Lambda@Edge is real AWS Lambda: Node.js or Python, the AWS SDK in the environment, outbound network access, and the ordinary Lambda programming model. Functions must be authored in **us-east-1** and associated as a *published numbered version* — `$LATEST` is rejected — after which CloudFront replicates them. The budgets differ by trigger: viewer-facing triggers get a small memory allowance and a few seconds of timeout, while origin-facing triggers get much more memory and tens of seconds, because they sit off the hot path of cache hits. Request and response bodies can be inspected, within size limits, when body inclusion is enabled. Things only Lambda@Edge can do: choose the origin dynamically per request, rewrite the request the origin sees, call DynamoDB or Secrets Manager for a real authorization decision, transform an origin response before it is cached, or generate a response from data fetched elsewhere. ```javascript export const handler = async (event) => { const request = event.Records[0].cf.request; const country = request.headers['cloudfront-viewer-country']; if (country && country[0].value === 'DE') { request.origin.custom.domainName = 'eu.origin.example.com'; request.headers.host = [{ key: 'host', value: 'eu.origin.example.com' }]; } return request; }; ``` ## How to choose Ask three questions in order. *Which trigger do I need?* If the answer is origin request or origin response, the choice is made — Lambda@Edge. *Do I need to call anything, or read a body?* If yes, Lambda@Edge again. *Otherwise* — a URI rewrite, a redirect, header work, a cache-key tidy-up — use a CloudFront Function, because it is dramatically cheaper per invocation, adds effectively no latency, and cannot become a source of edge timeouts. A common production shape puts a CloudFront Function on viewer request to normalize and reject junk, and a Lambda@Edge on origin request for the one decision that genuinely needs data. ## Operational gotchas A Lambda@Edge function is replicated to many Regions, so its logs land in CloudWatch Logs in the Region that served the request, not in us-east-1 — a recurring surprise during incident triage. Deleting a replicated function requires removing the association and waiting for replicas to clear. And any code on a viewer trigger is in the path of every cache hit, so a slow dependency there turns a CDN into a bottleneck.

  • You add a viewer-request function that calls an authorization service. Why is that a design smell?
    Two reasons. Viewer-request runs on every request including cache hits, so you pay the call and its latency even when CloudFront answers from cache. And CloudFront Functions cannot make network calls at all, so the design forces you onto Lambda@Edge with its viewer-trigger timeout. If the decision must be per request, budget for it deliberately or move it to origin request.
  • Where do Lambda@Edge logs actually appear?
    In CloudWatch Logs in the AWS Region that handled the request, under a log group name that includes the replicated function, not in us-east-1 where you published it. During an incident you either check the Region nearest the affected viewers or aggregate across Regions. It surprises people who go looking in us-east-1 and find nothing.
  • Why must Lambda@Edge be associated as a numbered version rather than $LATEST?
    CloudFront replicates the code to many Regions, so the association has to point at immutable content. A mutable alias would mean different edge locations running different code during a deploy. You publish a version, associate it, and the deploy becomes an explicit replication step you can roll back by re-associating the previous version.

saying these in an interview costs you the question

  • Thinks CloudFront Functions can call other AWS services
  • Claims either runtime can hook all four trigger points
  • Believes viewer-request code only runs on cache misses
  • Says Lambda@Edge can be associated from any Region
  • Assumes edge functions can read the request body by default

context

open as a page

In Amazon CloudFront, when would you protect private content with signed URLs versus signed cookies, and what does a trusted key group have to do with either?

level: middleimportance: must knowfreq 62%

basics

~20 s

CloudFront signed URLs authorize one object each and change the URL; signed cookies authorize many objects and leave URLs unchanged. Both are verified by CloudFront against the public keys in the trusted key group attached to that cache behavior.

open as a page

You are adding cdn.example.com to a CloudFront distribution as an alternate domain name, but the ACM certificate you issued in eu-west-1 does not appear in the certificate list. Why, and what do you do about it?

level: juniorimportance: should knowfreq 42%

basics

~10 s

ACM certificates are Regional, and CloudFront reads viewer certificates only from us-east-1 (N. Virginia). Request or import the certificate again in us-east-1, covering cdn.example.com, then select it on the distribution.

open as a page

How do you put AWS WAF and AWS Shield in front of an Amazon CloudFront distribution, and what does each of them actually protect you from?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Create an AWS WAF web ACL with CloudFront scope (managed from us-east-1) and associate it with the distribution; it filters application-layer requests at the edge. Shield Standard protects network and transport layer floods automatically; Shield Advanced adds paid response support and cost protection.

open as a page

For a subscription streaming service, how would you decide between enforcing entitlement with CloudFront signed cookies at the edge and having every media request authorized by your own API?

level: principalimportance: should knowfreq 28%

basics

~20 s

Edge enforcement trades revocation and audit granularity for cache efficiency and latency. Authorize once at your API, issue short-lived signed cookies, and keep per-request checks at the origin only where an immediate decision genuinely matters.

open as a page

CloudFront offers a built-in geographic restriction setting and AWS WAF offers a geo match rule. What can each do that the other cannot, and what would you tell a stakeholder who calls either one a compliance guarantee?

level: seniorimportance: nice to knowfreq 34%

basics

~20 s

CloudFront geo restriction is a free country allowlist or blocklist applied to the whole distribution. A WAF geo match rule is per-rule, combinable with path and rate conditions, and costs WAF pricing. Both infer country from IP address, so neither is authoritative.

open as a page