The declared Content-Encoding may not match the body
What this check looks for
The response claims to be compressed, but the body we measured was the same size as the uncompressed one. Either nothing was actually compressed, or our client quietly decompressed it before we could weigh it.
Why it matters
If the label is genuinely wrong, a client that trusts it tries to decompress data that is not compressed and the response fails to load. If the label is right, nothing is wrong at all — which is why this is reported as something to check rather than something to fix.
When the check passes, your report says: “The declared Content-Encoding matches the body we measured”.
What it costs your score
When this check fails it removes 6 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.compression family ceiling of 18 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
- medium
- Default confidence
- medium
- Status when triggered
- warn
- Deduction
- 6 points
- Family cap
- http.compression · 18
- Category
- HTTP
- Module
- Http headers
- Fix owned by
- user
- In the ruleset since
- 2026.09
How to fix it
Confirm in a browser whether the response really is mislabelled, then fix the layer that labels it.
A Content-Encoding that does not describe the body makes the response undecodable for clients that trust it.
Open the URL in a browser first. A content-decoding error confirms a real fault; a page that loads normally means our client decoded the body transparently and there is nothing to fix.
To see the wire bytes rather than the decoded ones, request without offering compression and compare:
curl -sS --compressedagainstcurl -sS -H 'Accept-Encoding: identity'.If it is real, look for two compressing layers — an application compressing the body and a proxy or CDN compressing it again. Compress in exactly one place.
Never set
Content-Encodingby hand as a static header; it must be emitted by whatever actually performed the compression.
How to confirm it worked
Open https://‹host›/ in a browser and confirm there is no content-decoding error
curl -sS -o /dev/null -w '%{size_download}\n' -H 'Accept-Encoding: identity' https://‹host›/ and compare against the same request with
Accept-Encoding: ‹declared encoding›
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
gzip on;
gzip_vary on;
gzip_min_length 256;
gzip_comp_level 5;
gzip_types text/plain text/css text/xml application/javascript application/json image/svg+xml;
# Brotli, if the ngx_brotli module is available:
# brotli on;
# brotli_types text/plain text/css text/xml application/javascript application/json image/svg+xml;<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/css text/xml application/javascript application/json image/svg+xml
</IfModule>
<IfModule mod_brotli.c>
AddOutputFilterByType BROTLI_COMPRESS text/html text/css application/javascript application/json
</IfModule>encode zstd gzipSpeed → Optimization → Content Optimization, or Rules → Overview → Create rule → Compression Rule to choose the algorithm per path. Dashboard paths as at 2026-09.text/htmlis always compressed by nginx and must not be listed ingzip_types— listing it logs a duplicate-MIME warning. Brotli is not part of a stock nginx build:brotli on;fails to start a server without the ngx_brotli module compiled in, so leave those lines commented untilnginx -Vshows it.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. mod_deflate adds
Vary: Accept-Encodingon its own. mod_brotli ships with Apache 2.4.26 and later but is not enabled by default —a2enmod brotli, or the equivalent LoadModule line, is required, and the IfModule guard keeps the configuration valid until then.Caddy's
encodedirective negotiates the encoding and setsVary: Accept-Encodingitself. A stock Caddy build offers gzip and zstd; Brotli is not one of its on-the-fly encoders, so do not writeencode brand expect it to work.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. Cloudflare compresses eligible responses by default, so a response measured as uncompressed here usually means the scan reached the origin directly.
Technical detail
**Read the confidence on this one.** The response declared Content-Encoding: ‹declared encoding›, and the body was the same length as the one returned for Accept-Encoding: identity. ‹detail›
What was actually measured is two byte counts: the length of the body received with Accept-Encoding: ‹declared encoding›, and the length received with Accept-Encoding: identity. They were equal. What was **not** measured is the bytes on the wire, because the edge fetch API this scanner uses may transparently decode a compressed body and hand us the decoded length — in which case the two counts match precisely when everything is working correctly. That ambiguity is why this rule reports at medium confidence and why it is a warning rather than a failure.
Two things distinguish the real fault from the artefact. A genuinely mislabelled response fails to load in a browser with a content-decoding error, so open the URL and look. And double compression — a proxy compressing a body an origin already compressed, then declaring the encoding once — produces a body that is smaller but still undecodable; the browser shows the same error.
If the page loads normally in a browser, this is the measurement artefact, not a defect. 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.
Other http headers checks
- The caching instructions contradict each other
- A text response does not declare its character encoding
- A compressible response was sent uncompressed
- The response has a body but no Content-Type
- The response sends obsolete headers, one of them harmful
- No Alt-Svc header advertises HTTP/3
- A response header leaks an internal address
- The response does not say how long it may be cached
- The response cannot be revalidated without downloading it again
- A compressed response does not vary on Accept-Encoding
- Vary: * makes the response uncacheable by any shared cache