The server is scaling, but the file still is not cached
After a release, latency and the number of origin requests increase. The team adds capacity, yet images, CSS, or other files that the CDN should serve account for most of the traffic.
Someone then opens one static-file URL and refreshes it several times. Every refresh appears in the application logs. The CDN is in the request path, but it is not reducing the load.
A few response headers can often localize this problem. The fix still requires care: a broad rule that ignores signs of personalization may store one user’s response and deliver it to someone else.
Start with one control URL
Choose a file that should unquestionably be public, such as CSS with a content hash in its name or an image with a stable address. Do not begin with the home page, where personalization, experiments, and authentication may overlap.
Make two identical GET requests without purging the cache between them:
curl -sS -D - -o /dev/null https://example.com/assets/app.4f31.css
curl -sS -D - -o /dev/null https://example.com/assets/app.4f31.css
Save Cache-Control, Set-Cookie, Vary, ETag, Last-Modified, Age, and your CDN’s cache-status header. At the same time, check whether both requests reached the origin log. Cache-status header names vary by provider, so the sequence of results matters more than the name.
A CDN makes the decision at two moments
Before contacting the origin, the CDN can see the method, URL, query parameters, request headers, and its configured rules. It uses them to decide whether it can look for a stored response and builds a cache key according to its configuration. Not every header automatically becomes part of that key; the policy depends on the CDN.
A second group of evidence appears after the origin replies: the status code and response headers. At this point, a file with a safe-looking path may turn out to be unsuitable for a shared cache.
Modern CDN controls can also apply rules at this response stage. Cloudflare’s new capability is a current example, but the underlying model is not tied to one provider. A response rule still cannot undo a cache bypass if the CDN already classified the request as ineligible during the request phase, so inspect both decisions.
What the suspicious headers mean
Set-Cookieoften causes a CDN not to store a response under the product’s default policy. On a static route, it may come from shared middleware that creates a session for every request. Do not remove the header with a rule until you have proved that the cookie is accidental and does not affect the content.Cache-Control: no-storeprohibits storing the response.privateprevents a shared cache from reusing it for different users.no-cachepermits storage but requires validation before reuse.Varyidentifies which request headers select a representation. Missing or incorrectly supported variation can serve the wrong representation, while an overly broad value can reduce the cache hit ratio. Support for individual values differs by CDN.ETagandLast-Modifiedare validators. After freshness expires, the CDN may send a conditional request and receive304 Not Modified. This reduces transferred data, but the request still reaches the origin.Agecarries an estimate of the seconds since the response was generated or last successfully validated at the origin. If it does not increase or keeps resetting, compare it with the cache status and origin logs.
Classify routes before making a fix
Public immutable assets contain the same data for everyone. Ideally, every content change gives the asset a new URL, commonly through a hash in the file name.
Shared dynamic content is also identical for its users, but it becomes outdated quickly. A short freshness period followed by revalidation may be appropriate.
Shared variants may depend on language or region without being personal. Cache them only when the CDN separates the variants through distinct URLs or cache keys.
Personalized responses depend on an account, authorization, cookies, or permissions and contain user-specific data. They must not become shared responses merely to raise the cache hit ratio.
Fix the origin and keep any CDN rule narrow
The primary fix should happen where the incorrect header originates. For versioned public assets, a typical candidate is:
Cache-Control: public, max-age=31536000, immutable
That route should not accidentally return Set-Cookie. The immutable directive is appropriate only when a content change produces a new URL.
For shared dynamic content, use a shorter s-maxage and validators if the response is guaranteed to be identical. For variant content, verify that the cache key really separates the variants. For personalized content, retain private or no-store according to the application’s requirements.
If the origin cannot be changed immediately, an edge response rule can be a temporary option. Restrict it to a known host, path, response type, and successful status code. Do not remove Set-Cookie or rewrite private globally.
Verify the change with a canary deployment
Apply the rule to one route or a small portion of traffic first. Record the initial cache hit ratio, origin request rate, latency, and errors. After the change, verify the file’s freshness and compare its checksum with the expected release artifact.
Run an anonymous request, requests from two test accounts, and every expected language or regional variant separately. Data crossing between users, the wrong variant, an old file surviving a release, new errors, or an unexpected status code should trigger immediate rollback. A higher cache hit ratio does not compensate for a correctness or security failure.
Dangerous shortcuts
- Do not force caching based only on a file extension. The route might return a login page or an error response.
- Do not treat
304as a complete cache hit. It may require revalidation with the origin. - Do not share language or regional variants until you have verified
Vary, distinct URLs, or the cache key. - Do not assign long freshness to a URL whose content changes without a new address.
- Do not measure success only by the share of cached responses. Check origin load, errors, freshness, and the absence of personalization leaks.
Sources
Quick checklist
- choose one public control URL
- save headers from two consecutive responses
- check the origin request logs
- classify the route before changing rules
- test anonymous and authenticated requests
- define metrics and rollback conditions
Find out why a CDN does not cache a response
Help me investigate why a specific URL does not remain in the CDN cache. First request these inputs: - the URL or path pattern without secret parameters; - the expected content type and update method; - whether the response depends on a user or the `Cookie` or `Authorization` headers; - headers from two consecutive responses; - `Cache-Control`, `Set-Cookie`, `Vary`, `ETag`, `Last-Modified`, `Age`, and the CDN cache-status header, if available; - relevant origin request logs; - available metrics and constraints on changing the application or CDN. Do not recommend forced caching until you determine whether the response is identical for everyone, varies by a safe dimension such as language or region, or contains personalized data. Mark assumptions and missing evidence. Return the result in this format: 1. Short conclusion. 2. Evidence table: observation, value, practical consequence. 3. Most likely cause and alternative causes. 4. Primary origin fix with exact candidate headers. 5. Narrow temporary CDN rule if the origin cannot be changed now. 6. Canary plan, success metrics, and rollback conditions. 7. Checks that prevent personalized responses from entering a shared cache.