Everything so far lives in one region. But your users don't. A request from Sydney to eu-central-1 pays ~300ms of physics before your code even runs — and it pays it on every image, script, and font. The fix isn't faster servers; it's moving the answer closer to the question. That's the edge layer: CloudFront (the CDN), plus the certificates and the WAF that ride along with it.
Caching: pay the distance once
CloudFront is hundreds of small caching sites ("edge locations") around the planet. Users connect to the nearest one. If the edge has a fresh copy of the response — a cache hit — it answers in a few milliseconds. If not — a miss — it fetches from your origin (the ALB or an S3 bucket), serves it, and keeps a copy for the response's TTL.
The hit/miss game
You're in Helsinki; the origin ALB is in us-east-1. Request the same asset a few times, then invalidate the cache and watch the miss come back. Try turning the CDN off for contrast.
requests
0
hits
0
misses
0
last latency
—
Two practical notes. First, caching is controlled by HTTP headers your origin sends (Cache-Control: max-age=86400) plus CloudFront's cache policy — a stampede of "why isn't my deploy visible" tickets traces back to over-long TTLs on HTML. The convention: cache static assets aggressively with hashed filenames (app.3f9c2.js can be cached forever, a new build gets a new name), keep HTML short-lived. Second, CloudFront isn't only for static files — it also speeds up dynamic, uncacheable requests, because users get TLS-terminated at the nearby edge and ride AWS's backbone to your origin instead of the public internet.
TLS: who holds the certificate, and where does encryption stop?
HTTPS isn't one connection end to end — it's a chain of hops, and at each hop that terminates TLS, traffic is decrypted, inspected, and re-encrypted (or not) toward the next hop. Knowing where termination happens tells you where certificates live (in ACM, AWS Certificate Manager — free, auto-renewing) and where traffic is readable.
Trace the encryption
Toggle the last leg. Many teams speak plain HTTP from the ALB to targets ("the VPC is private anyway"); compliance regimes often say otherwise.
encryptedcleartext
Each green segment is a separate TLS session — CloudFront and the ALB each decrypt, read the request (that's how routing rules and the WAF can inspect it), and open a new connection onward. Right now the final hop is cleartext inside the VPC.
WAF: the bouncer that reads the request
Because CloudFront and the ALB terminate TLS, they can see full HTTP requests — which means they can host a Web Application Firewall. WAF rules match on request content: SQL-injection patterns, XSS payloads, bad bots, rate limits per IP, geo blocks. This is Layer 7 filtering; security groups (Layer 3/4) can't do any of it — an SG sees ports and IPs, never a request body.
Attack yourself
Send each request with the WAF on and off. Note that the SG would have allowed all of these — they're all just "TCP 443 from the internet."
Layered defense, assembled: Route 53 answers the name → CloudFront absorbs distance and floods → WAF filters hostile requests → the ALB's SG admits only 443 → listener rules route → the target's SG admits only the ALB → the task answers from a private subnet. Seven layers, and you now know every one of them.
Check yourself
1. You deployed new CSS but users still see the old design. The file is served via CloudFront as styles.css with a 24h TTL. Best immediate + long-term fix?
Invalidation fixes today; content-hashed filenames fix forever — each deploy produces a brand-new URL that's never been cached, while old assets stay cached harmlessly. TTL 0 works but throws away the CDN's entire benefit.
2. Why can't a security group block SQL-injection attempts?
A SQLi request is a perfectly normal TCP connection to port 443 from the SG's point of view. Inspection requires decryption, and decryption happens where TLS terminates — which is exactly where AWS lets you attach a WAF.
3. Your API is fully dynamic — no cacheable responses. Is CloudFront still worth considering?
TCP+TLS handshakes are round-trip-heavy; doing them against a server 20ms away instead of 150ms away helps every request, cached or not. Add edge-level WAF/Shield and origin cloaking, and "CDN for a pure API" is a common, deliberate choice.