The response cannot be revalidated without downloading it again
Updated
What this check looks for
Your response carries no fingerprint, so a browser holding an old copy has no way to ask 'has this changed?'. It has to download the whole thing to find out.
Why it matters
Every repeat visit re-downloads bytes the visitor already has. On a slow connection that is the difference between a page that appears instantly and one that reloads from scratch. The exception is a page rebuilt for every response, as one whose Content-Security-Policy uses a per-response nonce is: it has nothing to revalidate.
When the check passes, your report says: “The response carries a validator, so it can be revalidated cheaply”.
What it costs your score
When this check fails it removes 4 points from your HTTP score, before the status, confidence and repeat multipliers are applied. HTTP carries a weight of 7 in the overall score.
It shares the http.caching family ceiling of 12 points: however many findings that family produces, together they cannot remove more than that from HTTP. One underlying problem showing up in several places is still one problem.
- Severity
- low
- Default confidence
- confirmed
- Status when triggered
- warn
- Deduction
- 4 points
- Family cap
- http.caching · 12
- Category
- HTTP
- Module
- Http headers
- Fix owned by
- user
- In the ruleset since
- 2026.09
How to fix it
Send an ETag or a Last-Modified date so caches can revalidate cheaply.
Without one, every freshness check costs a full download instead of a 304.
For static files, confirm the server has not been configured to strip them — nginx sends
ETagandLast-Modifiedby default (etag on;), and Apache sends both for files on disk.For a dynamic response, compute a strong
ETagfrom a hash of the body, or a weak one from the underlying record's version or updated-at timestamp.Handle the conditional request as well as emitting the validator: an
ETagnobody checks againstIf-None-Matchsaves nothing.Pair it with
Cache-Control: no-cacheon documents — that combination is what makes revalidation both mandatory and cheap.
How to confirm it worked
curl -sSI https://‹host›/ | grep -iE '^(etag|last-modified):'
curl -sSI -H 'If-None-Match: "<the etag you just saw>"' https://‹host›/ — expect 304 Not Modified
Cache-Control: no-cache
ETag: "a1b2c3d4"A named slot like ‹domain› — and the braces left in the configuration below — is filled in with your own values when this rule appears on a report.
Remediation by platform
location ~* "\.[0-9a-f]{8,}\.(?:css|js|woff2|png|jpg|svg)$" {
add_header Cache-Control "public, max-age=31536000, immutable" always;
}
location / {
add_header Cache-Control "no-cache" always;
}<IfModule mod_headers.c>
<FilesMatch "\.[0-9a-f]{8,}\.(css|js|woff2|png|jpg|svg)$">
Header always set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
<FilesMatch "\.(html|htm)$">
Header always set Cache-Control "no-cache"
</FilesMatch>
</IfModule>@immutable path_regexp \.[0-9a-f]{8,}\.(css|js|woff2|png|jpg|svg)$
header @immutable Cache-Control "public, max-age=31536000, immutable"
header Cache-Control "no-cache"Cache Rules → Create rule → set Browser TTL and Edge TTL per path, or Rules → Overview → Create rule → Response Header Transform Rule → Set static → Cache-Control. Dashboard paths as at 2026-09.Two rules, not one blended value. Note that an
add_headerinside alocationblock discards everyadd_headerinherited from the enclosing server block, so each location must repeat the headers it needs.immutableis only safe for a URL that changes whenever its content does.Shown because the response identified Apache. Put it in the virtual host or server configuration rather than .htaccess: .htaccess is re-read on every request, is often disabled by AllowOverride None, and runs too late to influence a response the virtual host generates itself. It also needs mod_headers loaded; the IfModule guard means the configuration still parses when it is not, but the header is then simply absent.
Caddy applies the more specific matcher first; the unmatched rule is the fallback.
This applies only to responses that reach visitors through Cloudflare. An origin that is reachable directly — by IP, or through a DNS record that is not proxied — still serves the response measured here unchanged. Setting it at the origin as well covers both paths. A Cache Rule also changes what Cloudflare itself stores, which a Transform Rule does not — decide which of the two you actually mean.
Technical detail
Neither ETag nor Last-Modified was present on the response from ‹origin›. ‹detail›
A validator is what turns a cache check into a 304 Not Modified: the client sends If-None-Match with the entity tag, or If-Modified-Since with the date, and a server that recognises it answers with headers and no body. Without one, no-cache — which mandates revalidation — degrades into a full re-download every single time, which is the opposite of what it was set for.
Most servers do this for static files automatically; a response missing both validators usually comes from an application that builds it dynamically. Note that on-the-fly compression can alter or weaken the entity tag, and that a weak validator (W/"…") still supports revalidation perfectly well — it merely cannot be used for byte-range requests.
One kind of page is reported as a note instead of a warning: an HTML page whose Content-Security-Policy puts a nonce in script-src, style-src or default-src. A nonce is generated for each response and written into the page, so the body differs every time by design and no validator from one response matches the next. A validator that did match would do harm: when a 304 carries the policy header, a cache updates the stored response's headers from it (RFC 9111 §4.3.4), so a fresh policy with a new nonce would sit on top of an old body carrying the old one, and the page's own scripts would be blocked. For such a page, no validator is the correct configuration. If a second request returns the same nonce, the page is not built per response and the warning stands. Detected server: ‹detected server›.
Standards and references
Test this on your domain
Run the check that produces this finding, on its own, against any domain.