11.2
Geo Restriction, Invalidation, VPC Origins and Signed URLs
Country allow/block lists, purging the cache, private origins in a VPC, signed URLs and cookies, and price classes.
24 min Reading + animations
Tip: tap or hover any dotted-underlined term to see what it means.
By the end of this lesson you will be able to
- Explain how CloudFront, Shield Standard, AWS WAF and Shield Advanced form layered DDoS protection at the edge, and pick the layer that stops a given attack.
- Use an ALB or EC2 instance as an origin without leaving a back door: the CloudFront managed prefix list, a secret origin header, and VPC origins for private ALBs, NLBs and EC2 instances.
- Configure geo restriction with an allow list or block list, predict the 403, and know when AWS WAF or an edge function is needed instead.
- Refresh cached content with invalidations, calculate what they cost, and prefer versioned file names when files change often.
- Serve private content with CloudFront signed URLs or signed cookies, choose between them and between canned and custom policies, and compare them with S3 pre-signed URLs.
- Cut cost with price classes and give viewers friendly custom error pages.
#CloudFront as the single front door
Lesson 11.1 showed CloudFront as a cache: edge locations close to viewers, an origin far away, a cache key and a TTL. This lesson is about everything else a distribution can do once all traffic flows through it. Because every request now arrives at an edge first, that edge is the natural place to filter attacks, check where the viewer is, check whether the viewer has paid, and decide what to show when something fails.
All of that only works if the edge is the only way in. A WAF rule, a geo restriction or a signed URL protects nothing if someone can send requests straight to the ALB or the bucket behind it. So this lesson keeps returning to one question: can anyone skip CloudFront?
The running example is a Dhaka online shop at shop.example.com, served by the distribution d111111abcdef8.cloudfront.net with an Application Load Balancer origin in ap-south-1 (Mumbai), plus a streaming service, stream.example.com, that sells films with country rights.
#DDoS protection at the edge
A DDoS (distributed denial of service) attack tries to make a service unavailable by flooding it from thousands of machines at once. Floods come in two broad kinds:
- Network and transport floods (layers 3 and 4): SYN floods, UDP floods, reflection attacks. They try to exhaust bandwidth or connection tables, and never become a valid HTTP request.
- Application floods (layer 7): real HTTP requests, each one valid on its own (
GET /searchfifty thousand times a second), plus targeted attacks such as SQL injection hidden inside a request.
No single control stops both, which is why AWS stacks three of them in front of the origin.
#Shield Standard: free, always on
AWS Shield Standard protects every AWS customer at no extra charge, with nothing to turn on. It defends against the common layer 3 and 4 attacks, such as SYN and UDP floods and reflection attacks. The notes say it comes with CloudFront; more precisely, it covers all AWS customers and resources, and the protection is strongest at the edge services, CloudFront, Route 53 and Global Accelerator, because the attack is absorbed by AWS's capacity across hundreds of locations instead of one Regional front door.
That is the first reason to put CloudFront in front of an ALB even when caching hardly matters: a flood aimed at the shop lands on the CloudFront edge network, and the ALB in Mumbai only ever sees completed, well-formed HTTP requests.
#AWS WAF: reading every HTTP request
AWS WAF (web application firewall) inspects layer 7: the path, query string, headers, cookies and body of each request. You attach a web ACL (access control list) to the distribution; it holds rules that Allow, Block or Count (or challenge) matching requests:
- AWS managed rule groups for common attacks: SQL injection (SQLi), cross-site scripting (XSS), known bad inputs, bad bots, and the anonymous IP list (VPNs, proxies, hosting providers).
- Rate-based rules: block a source IP (or another key) that sends more requests than a limit you set over a short window. This is the tool against HTTP floods.
- IP sets, geo match (country and region) and string or regex matches on any part of the request.
A web ACL for CloudFront has global scope and is managed in us-east-1, like the ACM certificates for CloudFront. WAF is not only for CloudFront: lesson 6.2 lists the ALB, API Gateway, AppSync, Cognito user pools, App Runner, Verified Access and Amplify, and notes that it does not attach to an NLB or a Classic Load Balancer. On CloudFront it runs at the edge, so a blocked request never crosses the ocean to your origin. You pay per web ACL, per rule and per million requests inspected.
#Shield Advanced: the paid tier
AWS Shield Advanced adds protection for organisations that cannot afford to go down:
- Protected resources you choose: CloudFront distributions, Route 53 hosted zones, Global Accelerator accelerators, Application and Classic Load Balancers, and Elastic IP addresses (which covers EC2 instances and NLBs behind them).
- Better detection and mitigation tuned to each resource's normal traffic, including automatic application layer DDoS mitigation, which writes and adds WAF rules for you during an attack.
- The Shield Response Team (SRT), AWS DDoS specialists available 24/7. The notes call it the DRT (DDoS Response Team), its old name. Contacting the SRT needs Business or Enterprise Support.
- DDoS cost protection: if a DDoS attack on a protected resource drives up your scaling charges (more EC2 instances, more CloudFront or ALB usage), AWS returns them as service credits on request. The notes say AWS "refunds the money"; it is a credit against your AWS bill for the scaling charges, not a cash refund of the whole bill.
- WAF included: standard AWS WAF charges for protected resources are covered by the subscription.
Price: $3,000 a month per organisation (billed to the payer account, covering all its accounts), with a one-year commitment, plus data transfer out usage fees for protected resources. The notes' "about $3,000 a month" is right; the commitment and the extra usage fees are what people forget.
Three layers of defence at the edge
Shoppers and a botnet both reach the shop through the same CloudFront edge. Shield Standard filters network floods there, a WAF web ACL inspects every HTTP request, and only clean requests travel on to the ALB in Mumbai.
Scenario
A shopper opens a product page: every layer looks, none objects.
A shopper in Dhaka opens shop.example.com/products/42. DNS sends her to a nearby CloudFront edge, so the TCP and TLS handshakes finish close to her, not in Mumbai.
#Which layer stops which attack
| Attack or need | Layer | What stops it |
|---|---|---|
| SYN flood, UDP flood, reflection | 3 / 4 | Shield Standard (free), absorbed at the edge |
| SQL injection, cross-site scripting in a request | 7 | AWS WAF managed rule groups |
| HTTP flood: many valid requests per second | 7 | WAF rate-based rule, plus caching at the edge |
| Block an IP range or a country | 7 | WAF IP set or geo match (or CloudFront geo restriction for whole countries) |
| Large, sophisticated attack; expert help needed | 3–7 | Shield Advanced with the SRT |
| Avoid a surprise bill from attack-driven scaling | – | Shield Advanced DDoS cost protection |
| Central WAF and Shield rules for many accounts | – | AWS Firewall Manager (chapter 21) |
#ALB or EC2 as an origin
An S3 bucket origin is locked down with Origin Access Control (lesson 11.1): the bucket stays private and only the distribution can read it. An ALB or EC2 origin (a custom origin) needs its own answer, and there are two families of them: keep the origin public but reachable only from CloudFront, or make it private and let CloudFront reach into the VPC.
#Public origins and the CloudFront prefix list
Before VPC origins existed, CloudFront could only reach an origin over the internet, so the origin had to be public:
- EC2 as the origin: the instance needs a public IP, and its security group must allow the IP addresses of the CloudFront edge locations.
- ALB as the origin: the ALB must be internet-facing, and its security group must allow the edge locations. The EC2 instances behind it can stay in private subnets; their security group allows only the ALB's security group (lesson 3.5).
Allowing "the edge locations" used to mean copying CloudFront's IP ranges from ip-ranges.json (or the list at d7uri8nf7uskq.cloudfront.net/tools/list-cloudfront-ips) into the security group and updating them whenever AWS added ranges. Today you reference the AWS managed prefix list com.amazonaws.global.cloudfront.origin-facing (and com.amazonaws.global.ipv6.cloudfront.origin-facing for IPv6) in one inbound rule. AWS keeps it current, and it contains exactly the addresses CloudFront uses to talk to origins.
One practical catch: this prefix list has a weight of 55, meaning it counts as 55 rules toward the security group's quota of 60 inbound rules by default. Leave room, or ask for a higher quota.
#The hole: someone else's distribution
The prefix list stops random scanners and anyone hitting the ALB directly. It does not stop someone who knows CloudFront. An attacker can create a CloudFront distribution in his own account with your ALB's DNS name as the origin. His requests then reach your ALB from the same CloudFront origin-facing addresses as yours, and the security group lets them in. Your WAF rules, geo restriction and signed URLs all live in your distribution, so his requests skip every one of them.
#The secret header fix
AWS's recommended addition is a secret custom origin header:
Add a custom header in the distribution's origin settings
For example
X-Origin-Verifywith a long random value. CloudFront adds it to every request it sends to this origin. Viewers never see it.Make the ALB require it
Add a listener rule: if header
X-Origin-Verifyequals the value, forward to the target group. Change the default rule to a fixed 403 response (lesson 6.3). Requests without the header, including the attacker's own distribution, get 403.Keep it secret
Use HTTPS between CloudFront and the ALB so the header cannot be sniffed, keep the value in a secret store, and rotate it now and then (add the new header, accept both, remove the old one). A WAF web ACL on the ALB can check the header instead of a listener rule.
The prefix list plus the header together is the strongest public-origin setup: the prefix list blocks non-CloudFront traffic at the network layer, and the header blocks other people's distributions at layer 7.
#VPC origins: a private ALB, NLB or EC2
Since November 2024, CloudFront VPC origins remove the need for a public origin at all. The ALB (or NLB, or EC2 instance) sits in a private subnet with no public IP, and CloudFront reaches it privately:
- You create a VPC origin that points at the resource's ARN. CloudFront places a service-managed network interface in the private subnet (the subnet needs a free IPv4 address) and sends traffic from the edge to it over the AWS network, never over the public internet.
- The origin's security group allows inbound traffic from the service-managed security group
CloudFront-VPCOrigins-Service-SG(the tightest choice: only your distributions) or from the CloudFront prefix list. - An attacker has nothing to aim at: an internal ALB's name resolves only to private addresses, and his own distribution cannot use a VPC origin in your account. No public subnet, no secret header to rotate.
- No extra charge for VPC origins; you pay normal CloudFront rates.
The details that trip people up:
- The VPC must have an internet gateway attached, even though it is not used to route this traffic; AWS uses it to mark the VPC as allowed to receive internet traffic.
- Supported origins are ALB, NLB and EC2. A Gateway Load Balancer cannot be one, an NLB must have a security group, and an NLB with a TLS listener is not supported.
- VPC origins do not support gRPC traffic or Lambda@Edge origin request and response triggers, and inbound network ACL rules are not evaluated for this traffic (outbound rules are, for the return path).
- A VPC origin can be shared with other accounts with AWS Resource Access Manager.
The notes mention ALB and EC2; the slides add the NLB, and they are right: VPC origins support all three.
#Choosing how to lock the origin
| Public ALB, prefix list | Public ALB, prefix list + secret header | VPC origin (private ALB, NLB, EC2) | S3 with OAC | |
|---|---|---|---|---|
| Origin reachable from the internet | Only CloudFront addresses | Only CloudFront addresses | No | No (private bucket) |
| Another account's distribution can use it | Yes | No (403) | No | No |
| Maintenance | Rule weight 55 | Rotate the header | None | Bucket policy |
| Exam keyword | “allow only CloudFront IPs” | “prevent direct access, custom header” | “private subnet, no public exposure” | “keep the bucket private” |
Can anyone skip CloudFront?
The shop’s WAF, geo restriction and cache all live in its own distribution. An attacker who reaches the ALB some other way skips all of them. Compare a public ALB, a security group with the CloudFront prefix list, a secret header, and a VPC origin.
Scenario
The ALB’s security group allows 0.0.0.0/0 on 443: CloudFront works, but so does the back door.
The normal path: the shopper reaches the distribution, which checks WAF and geo rules and forwards a cache miss to the ALB’s public DNS name. The EC2 instance sits in a private subnet behind the ALB.
#Geo restriction
#Allow lists and block lists
Geo restriction (geoblocking) lets a distribution refuse viewers by country:
- Allow list: serve only viewers in the listed countries.
- Block list: serve everyone except viewers in the listed countries.
A distribution uses one or the other. A refused viewer gets HTTP 403 Forbidden straight from the edge, without the origin being involved. With a custom error response for 403 you can show a page such as “This film is not available in your country”; CloudFront caches that error for 10 seconds by default.
Two limits shape every design:
- It works only at country level.
- It applies to the whole distribution. If trailers should play everywhere but films only in some countries, you need two distributions, or AWS WAF, which can scope a rule to a path.
#How CloudFront decides the country
CloudFront looks up the viewer's IP address in a third-party geolocation database. AWS quotes about 99.8% accuracy overall, varying by country. Two consequences:
- A VPN defeats it. A viewer in Dhaka using a VPN server in the US arrives with an American address and passes a US-only allow list. If a contract demands more, add the WAF managed anonymous IP list rule group (known VPNs, proxies and hosting providers), and check the account's billing country in the app before it hands out a signed URL.
- Unknown addresses are served. If CloudFront cannot determine the location, it serves the request even with an allow list. Geo restriction is a licensing tool, not an access control on its own.
CloudFront can also pass the location to the origin without blocking anything: add headers such as CloudFront-Viewer-Country (or CloudFront-Viewer-City, CloudFront-Viewer-Country-Region) through an origin request policy, and the app can show prices in taka or dollars.
#Finer than a country: AWS WAF and edge functions
The notes say: to block a city or an IP address instead of a country, use AWS WAF. Half of that is right:
- IP addresses and ranges → WAF IP set rules. Correct.
- Regions inside a country (a US state, a Bangladeshi division) → a WAF geo match statement, which labels every request with its country and its ISO 3166-2 region, such as
US-TX; a label match rule then blocks or allows it. - Cities → not WAF: WAF geo match has no city level. Use an edge function that reads the
CloudFront-Viewer-Cityheader (lesson 11.3), or a third-party geolocation service in the app that hands out signed URLs only to allowed viewers.
One more interaction: requests refused by CloudFront geo restriction are never passed to WAF (allowed ones are). If you need geography combined with other conditions, or want WAF to count and log those requests, use the WAF geo match statement instead of geo restriction.
Geo restriction at the edge
A streaming service may show a film only where it holds the rights. The edge looks up each viewer’s IP address in a geolocation database and either serves the request or answers 403.
Scenario
A US studio licensed a film for the US only (the notes’ copyright example).
The distribution’s geo restriction is an allow list with one country: US. It applies to every file in the distribution.
Self-checkQuestion
A company blocks five countries with CloudFront geo restriction and also uses AWS WAF. The security team wants the WAF logs and metrics to show every request from those countries, and to let one partner IP range in one of them through. What should the architect do?
#Cache invalidation
#Why users see the old file
The notes' example: the shop's offer banner, /images/banner.jpg, is cached at the edges. With no Cache-Control from the origin and the default cache settings, CloudFront keeps it for the default TTL of 24 hours (86,400 seconds). At 10:00 marketing uploads a new banner to S3 under the same name. Shoppers keep seeing the old offer, because the edge has a valid copy and CloudFront is never told that the origin changed. An origin does not push updates; the edge only goes back after the TTL expires.
(The notes say CloudFront “caches it, which is called TTL”. The cache is the stored copy; the TTL is how long the copy counts as fresh.)
#Invalidating paths
An invalidation tells CloudFront to remove objects from every edge cache before their TTL runs out. The next request for them is a miss, and the edge fetches the current version from the origin.
aws cloudfront create-invalidation --distribution-id E1ABCDEXAMPLE \
--paths "/images/banner.jpg" "/index.html"
The rules of a path:
- It is relative to the distribution and starts with
/:/images/banner.jpg, not the full URL. *is a wildcard only as the last character:/images/*(everything under the folder),/images/banner*(all names starting with banner),/*(the whole distribution). On the command line, quote it.- Paths are case-sensitive:
/images/Banner.jpgis a different file. - If query strings are part of the cache key, each variant is a separate cached object;
/images/banner.jpg*covers them all.
An invalidation shows InProgress and then Completed, usually within seconds to a few minutes across the network. It only removes; it does not pre-load the new file.
#What invalidation costs
The notes are right that it costs money after a free amount. The exact rule:
- The first 1,000 invalidation paths per month are free, counted per AWS account across all its distributions.
- After that, each path costs $0.005.
- Every path counts separately, even several paths in one request. A path with a wildcard counts as one path, however many files it matches.
For example, three distributions that each invalidate 50 paths a day for 30 days submit 4,500 paths: 3,500 above the free amount, so 3,500 × $0.005 = $17.50 for the month. The same team invalidating /assets/* once per deployment would pay nothing.
#Versioned file names: the better habit
Instead of changing a file in place, give each version a new name: banner-v2.jpg, or a content hash such as app.3f9a1c.js that build tools add automatically. The HTML page references the new name, and only the HTML (with a short TTL, or one invalidated path) needs to change. AWS recommends this for files that change often, because:
- A new name is a new cache key, so every edge and every browser fetches it at once. Invalidation cannot reach browser caches or company proxies; a new name bypasses them.
- It costs no invalidation fees, and the versioned files can have a TTL of a year.
- Rolling back means pointing the HTML at the old name, which is still cached.
- Logs show exactly which version each viewer got.
The notes call this “Object Versioning”. They mean versioned file names; S3 object versioning (version IDs, lesson 10.4) is a different feature and does not help here, because CloudFront still requests the same key.
#Invalidation pitfalls
- Invalidating
/*at peak time. It is one free path, but every object at every edge becomes a miss at once. If the edges served 95% of 20,000 requests a second, the origin suddenly sees up to 20,000 instead of 1,000. Prefer narrow paths, Origin Shield (lesson 11.1), or versioned names. - Forgetting the browser. A
Cache-Control: max-ageof a day on the file means returning visitors keep the old copy for up to a day, whatever you invalidate. - Rate limits. CloudFront processes up to 150 paths (or tags) per second and one wildcard invalidation per second per account; very large invalidation jobs take a while.
- Paths after a rewrite. If an edge function changes the URI, invalidate both the original and the rewritten path.
Changing a cached file: wait, invalidate, or rename
The shop caches /images/banner.jpg for 24 hours (the default TTL of 86,400 seconds). At 10:00 marketing uploads a new Eid banner with the same name. Follow the S3 origin, the edge cache, a shopper’s browser cache and what she actually sees.
Scenario
No action: the edge keeps the old banner until its TTL runs out.
S3 origin
banner.jpg = old
Edge cache
old banner, TTL 24 h
Browser cache
nothing cached
Shopper sees
old banner: 20% off
Time
Day 1, 08:00
Invalidation
none
Paths this month
0
Origin load
normal
Invalidation cost calculator
The first 1,000 invalidation paths a month are free per AWS account (all distributions together); each further path costs $0.005. A path with a * wildcard counts as one, however many files it matches.
How you refresh the cache
Paths submitted
2,400
Free allowance
1,000
Billable paths
1,400
Monthly invalidation cost
$7.00
The wildcard is cheap but empties the whole folder at every edge; versioned names cost nothing and never serve a stale file, which is why AWS recommends them for files that change often.
The first shopper of the day asks for banner.jpg. Cache miss: the edge fetches it from S3 and keeps it for 24 hours.
#Signed URLs and signed cookies
#Restricting viewer access
OAC makes the bucket private, but the distribution itself is still public: anyone with the URL can watch the film. For paid or private content, a cache behavior can restrict viewer access: CloudFront then serves its files only to requests carrying a valid signed URL or signed cookies. Your application decides who may have access (login, subscription, purchase) and signs; CloudFront only checks the signature at the edge.
Because the check happens per cache behavior, one distribution can mix public paths (/trailers/*) and private ones (/films/*). Order the behaviors carefully: a request uses the first behavior whose path pattern matches.
#Key groups: who may sign
Signing uses a public–private key pair, RSA 2048-bit or ECDSA 256-bit:
- The private key stays with your application (in a secret store), which uses it to sign.
- The public key is uploaded to CloudFront and added to a trusted key group, which you attach to the cache behavior. Each edge uses it to verify signatures.
Use trusted key groups. The legacy alternative, a CloudFront key pair of the AWS account, has to be created by the root user, cannot be managed with the API or IAM, and allows only two active key pairs per account. A key group holds up to five public keys and a cache behavior can trust up to four key groups, so you can rotate keys without breaking URLs that are still valid: add the new key, sign with it, and remove the old one after its last URL expires.
#Canned and custom policies
Every signed URL or cookie carries a policy saying what it allows:
| Canned policy | Custom policy | |
|---|---|---|
| Resource | Exactly one URL | One URL or a pattern with * (/films/7/*) |
| Valid until | Yes (Expires) | Yes |
| Valid from (start time) | No | Yes |
| Viewer IP address or range | No | Yes |
| Size | Short URL | Longer (the policy itself is included, base64-encoded) |
#Signed URL or signed cookies
| Use a signed URL when | Use signed cookies when |
|---|---|
| Access is to one file: an installer, a printable ticket, an offline copy of a film | Access is to many files: all the segments of an HLS video, a members-only area |
| The client does not support cookies (a custom HTTP client, some players) | You do not want to change the URLs your pages already use |
A signed URL adds the query parameters Expires (or Policy for a custom policy), Signature and Key-Pair-Id. Signed cookies carry the same three values as cookies: CloudFront-Expires or CloudFront-Policy, CloudFront-Signature and CloudFront-Key-Pair-Id. If a request has both, the signed URL wins.
Remember that a signed URL is a bearer token: anyone holding it can use it until it expires. Keep expiry short, use a custom policy with the viewer's IP when that fits, and keep the origin private (OAC for S3, VPC origin or secret header for an ALB), or people will simply bypass CloudFront.
Signed URL and signed cookies
Only paying members may watch films on stream.example.com. The app signs access with a private key; every CloudFront edge checks the signature with the matching public key from a trusted key group. The bucket itself stays private behind OAC.
Scenario
A member downloads one file, the offline copy of a film.
The member logs in and asks to download one film. CloudFront knows nothing about members; the app decides who may have what.
#CloudFront signed URL vs S3 pre-signed URL
Lesson 10.9 introduced S3 pre-signed URLs. Both give temporary access to private content, but they work very differently:
| CloudFront signed URL or cookie | S3 pre-signed URL | |
|---|---|---|
| Signed with | Your own key pair (key group) | The IAM credentials of the signer (SigV4) |
| Acts as | Permission checked by CloudFront | The IAM principal that signed it |
| Goes to | A CloudFront edge, then any origin (S3, ALB, EC2, custom) | S3 directly, in one Region |
| Caching and global speed | Yes, at the edge | No |
| Restrictions | Path or path pattern, start, expiry, IP range | One object, one operation, expiry |
| Expiry | Any time you set | Up to 7 days, shorter with temporary credentials |
| Uploads | Mainly downloads | GET and PUT (uploads) |
The exam rule: private content delivered globally through CloudFront → CloudFront signed URLs or cookies (with OAC on the bucket). A temporary link to one S3 object, especially an upload → S3 pre-signed URL.
#Price classes
CloudFront prices differ by where an edge is. A price class limits which edge locations serve your distribution:
| Price class | Edge locations used |
|---|---|
| Price Class All (default) | Every edge location, including South America and Australia and New Zealand: best performance |
| Price Class 200 | North America, Europe, Israel and Türkiye, Africa and the Middle East, Japan, India and most of Asia; excludes the most expensive locations (South America, Australia and New Zealand) |
| Price Class 100 | Only the cheapest: the United States, Canada, Mexico, Europe, Israel and Türkiye |
A price class does not block anyone. A viewer outside the included areas is still served, from an included edge farther away, with more latency. For the Dhaka shop, Price Class 200 keeps Indian edges close to its customers; Price Class 100 would send them to Europe. Use Price Class 100 when almost all viewers are in North America and Europe and cost matters more than the rest.
#Custom error pages
When the origin returns an error, or CloudFront refuses a request, viewers normally see a bare error. A custom error response per status code (403, 404, 500, 502, 503, 504 and the other 4xx and 5xx codes) lets you:
- Return your own page, such as
/errors/503.html, stored on an origin that is not the one failing (an S3 bucket with its own cache behavior for/errors/*). - Change the status code the viewer receives. The classic case is a single-page application on S3 with OAC: a route such as
/orders/42is not an object, S3 answers 403 or 404, and a custom error response returns/index.htmlwith 200 so the app's router can show the page (lesson 10.3 did the same with the S3 error document). - Set the error caching minimum TTL: how long CloudFront caches the error before asking the origin again. The default is 10 seconds. Too long, and viewers keep seeing the error after the origin recovers; too short, and a failing origin gets hammered.
A custom error page is the polite last line, not availability. For real failover use an origin group (lesson 11.1), and for a whole-site outage, Route 53 failover (lesson 8.3).
#What goes wrong
| Symptom | Likely cause | Fix |
|---|---|---|
| Users still see the old file after an upload | TTL not expired, CloudFront never told | Invalidate the path, or switch to versioned file names |
| One user still sees the old file after an invalidation | Browser cache (max-age) or a company proxy | Versioned names; shorter max-age for files changed in place |
| Origin overloaded right after a deployment | Invalidated /* at peak, every object a miss | Narrow paths, Origin Shield, versioned names |
| Invalidation bill appears | More than 1,000 paths a month in the account | Wildcards for whole folders, versioned names |
| WAF rules ignored by some traffic | Origin reachable directly, or through another distribution | Prefix list + secret header, or a VPC origin |
| Security group rejects the prefix list rule | Prefix list weight 55, 60-rule quota | Remove rules or request a higher quota |
| VPC origin cannot be created or reached | No internet gateway on the VPC, no free IP in the subnet, NLB without a security group or with a TLS listener | Attach an internet gateway, free an address, fix the NLB |
| Viewers in an allowed country get 403 | Geolocation maps their address elsewhere, or WAF blocks them | Check WAF logs, the geolocation, the allow list |
| Viewers in a blocked country get through | VPN or an address of unknown location | WAF anonymous IP list, app-side checks, signed URLs |
| Everyone gets 403 after adding a key group | The behavior now requires signatures and the app is not signing yet | Sign before restricting, or restrict a separate path |
| Signed URL works in one place, 403 for a friend later | It expired, or a custom policy pins the IP | Issue a new URL; this is intended |
| Error page stays after the origin recovered | Error caching minimum TTL too long | Lower it (default 10 seconds) |
#CloudFront features in system design
- One hardened entry point. CloudFront with WAF and Shield in front of everything (S3 with OAC, ALBs as VPC origins), Route 53 alias records pointing at the distribution. Every request passes the same checks, and the origins have no public exposure. This is the standard answer for a public web application on AWS.
- Rights-managed media. Geo restriction (or WAF geo match) for licensing, signed cookies for paying members, OAC on the bucket. Add the WAF anonymous IP list when VPN viewers matter.
- Deployment pipelines. Build hashed file names for assets with a one-year TTL, short TTL for HTML, and invalidate only
/index.htmlon each release. Releases become instant and nearly free. - Cost control. Price class to match the audience, caching to cut origin traffic, and Shield Advanced only where a large DDoS or its bill would really hurt.
- Graceful failure. Custom error pages from a separate S3 origin, origin groups for failover, and a short error caching TTL so recovery is visible quickly.
- When CloudFront is the wrong tool. For non-HTTP traffic such as UDP games, or when clients need fixed IP addresses, use Global Accelerator (lesson 11.4), which also has Shield protection at its edge.
#Try it yourself
With the AWS CLI and a distribution E1ABCDEXAMPLE (d111111abcdef8.cloudfront.net):
# 1. Is it cached? Look at x-cache (Hit / Miss / RefreshHit from cloudfront), age and x-amz-cf-pop
curl -sI https://d111111abcdef8.cloudfront.net/images/banner.jpg | grep -iE 'x-cache|age|x-amz-cf-pop|cache-control'
# 2. Invalidate one file and a folder, then watch the status
aws cloudfront create-invalidation --distribution-id E1ABCDEXAMPLE \
--paths "/images/banner.jpg" "/assets/*"
aws cloudfront list-invalidations --distribution-id E1ABCDEXAMPLE
aws cloudfront get-invalidation --distribution-id E1ABCDEXAMPLE --id I2J3K4EXAMPLE
# 3. Geo restriction and price class live in the distribution config
aws cloudfront get-distribution-config --id E1ABCDEXAMPLE > dist.json
# edit .DistributionConfig.Restrictions.GeoRestriction:
# {"RestrictionType": "whitelist", "Quantity": 1, "Items": ["US"]}
# and .DistributionConfig.PriceClass: "PriceClass_200"
aws cloudfront update-distribution --id E1ABCDEXAMPLE \
--if-match <ETag from get-distribution-config> --distribution-config file://config-only.json
# 4. The CloudFront managed prefix list for the ALB's security group (weight 55)
aws ec2 describe-managed-prefix-lists \
--filters Name=prefix-list-name,Values=com.amazonaws.global.cloudfront.origin-facing
aws ec2 authorize-security-group-ingress --group-id sg-0123456789abcdef0 \
--ip-permissions 'IpProtocol=tcp,FromPort=443,ToPort=443,PrefixListIds=[{PrefixListId=pl-EXAMPLE}]'
# 5. Test the secret-header rule: direct to the ALB without the header should give 403
curl -sI https://shop-alb-123456789.ap-south-1.elb.amazonaws.com/ | head -1
# 6. A VPC origin for a private ALB
aws cloudfront create-vpc-origin --vpc-origin-endpoint-config \
'Name=shop-private-alb,Arn=arn:aws:elasticloadbalancing:ap-south-1:111122223333:loadbalancer/app/shop-int/0123456789abcdef,HTTPPort=80,HTTPSPort=443,OriginProtocolPolicy=https-only'
# 7. Signed URLs: upload a public key, make a key group, then sign
openssl genrsa -out private_key.pem 2048
openssl rsa -pubout -in private_key.pem -out public_key.pem
aws cloudfront create-public-key --public-key-config \
"CallerReference=$(date +%s),Name=stream-key-1,EncodedKey=$(cat public_key.pem)"
aws cloudfront create-key-group --key-group-config 'Name=stream-signers,Items=K2JCJMDEHXQW5F'
aws cloudfront sign --url https://d111111abcdef8.cloudfront.net/films/film7.mp4 \
--key-pair-id K2JCJMDEHXQW5F --private-key file://private_key.pem \
--date-less-than 2026-12-31T23:59:59Z
aws cloudfront sign makes a canned-policy signed URL locally; nothing is sent to AWS. Try the URL, then the same URL without its query string (403), then after the expiry time (403). The API still calls an allow list whitelist and a block list blacklist.
#Summary
Key takeaways
- CloudFront is only a shield if it is the only way in. Every feature in this lesson assumes the origin cannot be reached around it.
- Shield Standard: free, automatic, layers 3 and 4 (SYN and UDP floods), strongest at the edge. AWS WAF: layer 7 web ACLs with managed rules (SQL injection, XSS), rate-based rules, IP sets and geo match; global scope for CloudFront. Shield Advanced: $3,000 a month per organisation, one-year commitment, plus usage fees; SRT (formerly DRT) 24/7, automatic layer 7 mitigation, DDoS cost protection as service credits, WAF included.
- ALB or EC2 origins: public origin + security group with the CloudFront managed prefix list (weight 55), plus a secret custom header checked by an ALB listener rule, because another account's distribution passes the prefix list. Or VPC origins (since Nov 2024): private ALB, NLB or EC2, no public exposure, service security group, internet gateway attached, no extra charge. S3 origins use OAC.
- Geo restriction: allow list or block list, country level only, whole distribution, 403, by IP via a third-party database; VPNs bypass it and unknown locations are served. Regions and IP ranges → WAF; cities → an edge function with
CloudFront-Viewer-Cityor a third-party service. - Invalidation removes objects from all edges before the TTL ends: paths,
*only at the end, case-sensitive; 1,000 paths a month free per account, then $0.005 per path, a wildcard path counts as one. It does not clear browser caches. For frequent changes use versioned file names. - Signed URLs (one file, clients without cookies) and signed cookies (many files such as HLS segments, unchanged URLs), signed with a private key whose public key is in a trusted key group (not root key pairs). Canned policy: one URL and an expiry; custom: wildcards, start time, IP range. Signed URL wins over cookies. Different from S3 pre-signed URLs, which use IAM credentials and go straight to S3.
- Price classes (All, 200, 100) limit which edges serve you to save money; viewers elsewhere are slower, never blocked. Custom error responses show your own page, can change the status code (SPA → 200), and cache errors for 10 seconds by default.
Answer on your own first, then read the explanation, even when you got it right.
Self-checkQuestion
Which protection does every CloudFront distribution get automatically, at no extra charge?
Self-checkQuestion
Security testing shows that the shop’s product search, served through CloudFront and an ALB, is vulnerable to SQL injection. The fix in the code will take weeks. What protects the application now with the least effort?
Self-checkQuestion
Which two benefits come with AWS Shield Advanced but not with Shield Standard?
Choose 2
Self-checkQuestion
A media company fears that a DDoS attack during a live event would make its Auto Scaling group and CloudFront usage, and so its bill, explode. Which service addresses this concern directly?
Self-checkQuestion
Which statement about the cost of AWS Shield Advanced is correct?
Self-checkQuestion
An online shop behind CloudFront receives 50,000 valid-looking GET /search requests per second from a few thousand IP addresses. Each request completes the TCP and TLS handshakes. Shield Standard is in place. What should the architect add?
Self-checkQuestion
A company serves its site through CloudFront from an internet-facing ALB. It must stop anyone from reaching the ALB directly, and it cannot change the ALB’s subnets this quarter. What is the simplest first step?
Self-checkQuestion
The ALB’s security group allows only the CloudFront managed prefix list, yet WAF logs show nothing for some suspicious requests that reached the application. They came from CloudFront addresses. What explains this, and what is the fix?
Self-checkQuestion
A new application must be served globally through CloudFront. The security team requires the ALB and its EC2 instances to have no public IP addresses and no internet-facing endpoint at all. Which design meets this with the least operational work?
Self-checkQuestion
Which two resources can be used as CloudFront VPC origins?
Choose 2
Self-checkQuestion
An engineer adds an inbound rule with the CloudFront managed prefix list to a security group that already has 10 rules. The change fails with a rules-per-security-group error. Why?
Self-checkQuestion
A film studio may show a new film only in the United States, because of copyright agreements. The film is served through CloudFront. What is the simplest solution?
Self-checkQuestion
A sports league must black out a live match for viewers in one US state, Texas, while the rest of the US can watch. The stream is served through CloudFront. What should the architect use?
Self-checkQuestion
The original notes say that to block one city instead of a whole country, the answer is AWS WAF. What is the accurate answer for a city?
Self-checkQuestion
With a US-only allow list in place, the rights holder complains that viewers in other countries still watch the film. Logs show their requests come from US addresses of a well-known VPN provider. Which statement is true?
Self-checkQuestion
A streaming service wants trailers under /trailers/* to play worldwide, while films under /films/* are allowed only in Bangladesh. Both are in one CloudFront distribution today. What works?
Self-checkQuestion
Which two statements about CloudFront geo restriction are true?
Choose 2
Self-checkQuestion
A developer replaced logo.png in the S3 origin an hour ago, but users still see the old logo. The object’s TTL is 24 hours. What gets the new logo to users fastest?
Self-checkQuestion
A news site updates its CSS and JavaScript bundles twenty times a day and invalidates them each time. The invalidation bill keeps growing and some readers still get old files. What is the best practice?
Self-checkQuestion
An AWS account has three distributions. Each one invalidates 50 individual file paths every day for 30 days. At $0.005 per path above the free amount, what is the month’s invalidation charge?
Self-checkQuestion
An object was cached at an edge at 08:00 with a TTL of 86,400 seconds. The origin file was replaced at 10:00 and nobody invalidated it. Assuming steady traffic, from when will viewers at that edge get the new file?
Self-checkQuestion
The team invalidated /images/banner.jpg and the status is Completed. One customer still sees the old banner on her laptop, while her phone shows the new one. What is the most likely reason?
Self-checkQuestion
At peak, edges receive 10,000 requests per second. The cache hit ratio rises from 90% to 98% after the cache policy is fixed. How do the requests that reach the origin change?
Self-checkQuestion
A subscription video service streams films as HLS: a playlist plus hundreds of segment files per film, whose URLs the player builds itself. Only logged-in subscribers may watch. What should it use?
Self-checkQuestion
A signed URL must work only for one customer’s office network, 203.0.113.0/24, and only from tomorrow morning. Which choice is required?
Self-checkQuestion
A training company sells course videos to students worldwide. Videos are in a private S3 bucket. It wants fast playback near students, caching, and links that expire after the session. Which is best?
Self-checkQuestion
Which two statements about CloudFront signed URLs and signed cookies are true?
Choose 2
Self-checkQuestion
A company’s viewers are almost all in the United States and Europe. It wants to lower CloudFront costs and accepts slower delivery for the few viewers elsewhere. What should it set?
Self-checkQuestion
A React single-page application is served from a private S3 bucket through CloudFront with OAC. Refreshing the browser on /orders/42 shows an XML Access Denied error. What fixes it?
Self-checkQuestion
Which two statements in the original notes needed correcting?
Choose 2