DDoS Resilience with HTTP caching on CloudFront
Written primarily with CloudFront in mind, this article describes the importance of HTTP caching in preventing malicious requests from reaching your origin as well as some of the CloudFront features and Cache-Control response header values that can help you optimize cache-hit ratio. It's a 'byte-sized' slice of the equivalent chapter the 'AWS DDoS Resiliency Whitepaper'.
Please read this article alongside DDoS Resilience - Using AWS WAF to Protect Against DDoS Botnets.
HTTP Caching with CloudFront
Amazon CloudFront's caching capabilities are a powerful tool for protecting your origin server's capacity. By implementing strategic caching policies — even with short time-to-live (TTL) values — you can significantly reduce the load on your origin infrastructure.
When content is served from CloudFront's cache hierarchy - edge POPs (points of presence closest to users), Regional Edge Caches (RECs, larger consolidated caching layers that sit between POPs and your origin)(with or without Origin Shield), requests may be served as cached objects from downstream peers in the network, preventing volume from reaching your origin. For content that isn't yet cached or has expired, CloudFront's native request collapsing further protects origins by consolidating multiple simultaneous requests into a single origin request.
This combination of edge caching and request collapsing means that even during high-traffic events or volumetric request floods, only a fraction of requests reach your origin — making CDN caching an essential strategy for origin capacity preservation.
There's a common misconception among content owners that their content isn't CDN-cacheable, when in fact it can be cached or made cacheable. Even when dealing with truly dynamic content delivered through asynchronous requests, the main page structure itself can and should be cached. Maximizing the cache hit ratio is crucial for both performance and DDoS resilience — it can be the determining factor between whether an HTTP request flood impacts your service or not.
Page cacheability should be established as a non-functional requirement during the initial planning stages of any website deployment.
Did you know? Statistically,
/is the most commonly targeted URI by bad actors in HTTP request floods.
The following solutions address common perceived blockers to caching static content:
-
Web pages requiring immediate updates on publish — manage content freshness using HTTP standard cache controls:
- Split TTL approach: Set
max-age=0in theCache-Controlresponse header so clients always check freshness on reload, while setting longer TTLs at the CDN level using thes-maxagedirective or a non-zero Minimum TTL in the relevant cache policy (the latter is preferred to enable request collapsing). - Microcaching: Use Minimum TTL values as low as 1–2 seconds for non-personalized but highly dynamic content such as election results or live streaming manifests.
- Conditional requests: Set
ETag/Last-Modifiedresponse headers to enable conditional requests, which optimize page load times and minimize Data Transfer Out charges by allowing HTTP 304 (Not Modified) responses with zero-length bodies when content hasn't changed. - Cache invalidations: Use CloudFront cache invalidations, which can be automatically triggered through CMS integrations during publish events. Consider this an option of last resort due to potential increased costs and concurrent invalidation limits.
- Split TTL approach: Set
-
Static assets (images, scripts, page resources) — implement unique file versioning with far-future client TTLs (1 year or more). Instead of updating files in place, publish new versions with unique URIs or query strings and update their references in HTML. This provides optimal delivery efficiency and maximum offload across browser caches, CDN, and origin while ensuring immediate updates through reference changes rather than invalidations.
-
Mixed content sites — create multiple cache behaviors with appropriate cache policies to maximize DDoS resilience. Cache static content aggressively. Identify truly dynamic endpoints that cannot be cached early, and protect them with scoped-down AWS WAF rate-based rules at lower limits than any catch-all rules.
-
Resources with
Cache-Control: no-cache, no-store— these can still be cached at the CDN by using a non-zero Minimum TTL in the relevant CloudFront cache behavior, without compromising security. -
Cookies in page responses — if you're currently serving cookies in responses to page requests, serve them from an asynchronous request instead.
-
Degraded origin performance — consider adding the
stale-while-revalidateCache-Controldirective so users no longer wait for origin responses. This is ideal for content that refreshes frequently or unpredictably, requires significant time to regenerate, or where serving the absolute latest version isn't essential. -
Origin errors — the
stale-if-errordirective improves availability by serving stale content when origins return errors.
Request Collapsing
Request collapsing occurs when multiple users simultaneously request the same content that isn't in cache. CloudFront sends only one request to the origin while briefly pausing the others. Once the origin responds, CloudFront serves the cached content to all waiting requests — preventing a flood of duplicate requests from overwhelming your origin during a typical DDoS attack. This is especially important when a requested URI path is not in cache or has expired.
The following configurations prevent request collapsing from occurring:
- The Minimum TTL of a cache behavior is set to 0.
- Cookie forwarding is enabled in the cache policy, the origin request policy, or the legacy cache settings.
A request flood attack combined with non-collapsed but otherwise cacheable requests can be sufficient to overwhelm some origins, especially those unable to dynamically auto-scale.
Origin Shield
Enabling CloudFront Origin Shield further reduces origin load by adding a centralized caching layer in a single regional edge cache. This is particularly useful for low-capacity origins without the ability to dynamically scale.
Error Pages and Error Caching
CloudFront caches certain 4xx and 5xx status codes returned by your origin for a default duration of 10 seconds. While caching server errors might seem counterintuitive, it prevents a snowball effect where viewer retries further degrade an already struggling origin. Alternatively, use the stale-if-error Cache-Control directive for content that is cached.
CloudFront also supports custom error pages. If you configure these, store them in a separate origin (e.g. an Amazon S3 bucket) so CloudFront can access them even when your primary origin is returning errors.
EC2 or S3-Based Caching
In edge cases such as requiring regional TLS termination, where a global CDN like CloudFront isn't suitable, it's still worth setting up a caching tier using S3 or dynamically scaled EC2 instances running a cache like Varnish to protect the content source.
Most major content management systems have plugins to replicate static content to S3. For a reference architecture on accessing S3 from an ALB without an EC2 tier, see the AWS blog post Hosting HTTPS Static Websites with ALB, S3, and PrivateLink.
- Language
- English
Relevant content
- Accepted Answer
asked 4 years ago
AWS OFFICIALUpdated 2 years ago
AWS OFFICIALUpdated a year ago