HTTP compression reduces the bytes sent for a response by encoding its body with an algorithm such as gzip or Brotli. For a website launch, verify that the deployed HTML, CSS, and JavaScript actually arrive compressed and still decode correctly. A small build output or a hosting dashboard switch does not establish what the browser receives.
This guide shows developers how to inspect compression, enable a basic gzip policy, debug delivery through a CDN, and keep a focused check in CI. Use the public release URL and real asset paths from that build.
HTTP compression launch checklist
- Identify the main HTML document and its largest text assets.
- Inspect the request's
Accept-Encodingand response'sContent-Encoding. - Verify the body decodes and has the expected media type.
- Compare encoded bytes with decoded bytes under a cold load.
- Check the final public response through the CDN, not only the origin.
- Confirm caches distinguish encoding variants appropriately.
- Save a repeatable release check for a substantial, stable text asset.
Compression belongs inside a website performance budget. It can reduce transfer cost, but unused JavaScript still has to be parsed and may execute after decompression.
Understand Accept-Encoding and Content-Encoding
A client advertises acceptable encodings; the server selects a representation. For example:
Accept-Encoding: gzip, br
A compressed JavaScript response might include:
Content-Type: text/javascript
Content-Encoding: gzip
Vary: Accept-Encoding
Content-Type identifies the underlying media. Content-Encoding describes the coding applied to its bytes. A script does not become application/gzip just because HTTP delivered it with gzip. RFC 9110's Content-Encoding definition explains how these fields work together.
Gzip uses the gzip token; Brotli uses br. The server can also deliver an uncompressed response when that representation is acceptable. Advertising Brotli does not force the server to select it. Check the returned coding instead of treating the request header as a result. MDN's Accept-Encoding reference covers negotiation and the identity representation.
When representation selection depends on Accept-Encoding, include it in Vary so caches can distinguish requests correctly. Preserve other required Vary values. Do not confuse body compression with HTTP/2 or HTTP/3 header compression, or with JavaScript minification. Removing whitespace and compressing the resulting response are separate operations.
Test gzip compression with curl
Start with the canonical HTTPS URL, without redirects, for a document you own:
curl --silent --show-error --fail --compressed \
--dump-header /tmp/compression-headers.txt \
--output /tmp/compression-page.html \
--write-out 'status=%{http_code} downloaded=%{size_download}\n' \
https://example.com/docs/getting-started
cat /tmp/compression-headers.txt
wc -c /tmp/compression-page.html
Replace the host and route. Check for 200, the intended HTML media type, and Content-Encoding. With --compressed, curl requests supported compression and decodes the saved body. The retained headers still describe the encoded response. Consequently, the saved file size can differ from the downloaded body size. The curl manual documents this behavior and its download measurements.
To inspect one specific coding without curl automatically decoding it:
curl --silent --show-error --fail \
--header 'Accept-Encoding: gzip' \
--dump-header /tmp/gzip-headers.txt \
--output /tmp/page.gz \
https://example.com/docs/getting-started
cat /tmp/gzip-headers.txt
Only if the response actually reports Content-Encoding: gzip, run:
gzip -t /tmp/page.gz
gzip -dc /tmp/page.gz > /tmp/page-decoded.html
wc -c /tmp/page.gz /tmp/page-decoded.html
A .gz output filename does not prove compression. An uncompressed response is still possible, and an HTML error page can be compressed successfully. Inspect status, media type, and decoded content together. Repeat for a current stylesheet and script, rather than assuming that the document policy covers assets.
For Brotli, use a client with Brotli decoding support and inspect whether the response selects br. curl --version lists the capabilities of your installed build. Do not advertise encodings a decoding test cannot handle.
Enable text compression in the response layer
Compress text responses that have meaningful savings: HTML, CSS, JavaScript, JSON, and SVG are useful candidates. Already compressed formats such as JPEG and ZIP usually offer little additional benefit. MDN's HTTP compression guide explains the distinction between file formats and HTTP response compression.
Choose the layer that owns delivery. On managed hosting, inspect the provider's compression settings. With a self-managed Nginx server, a starting configuration for public text responses could be:
# Inside the existing http block; adapt to the site's routing.
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 5;
gzip_types text/plain text/css text/javascript
application/javascript application/json image/svg+xml;
Nginx includes text/html automatically. Its gzip module documentation defines the MIME allowlist, minimum length, compression level, and Vary behavior. The values above are a starting policy, not a performance guarantee. Review configuration inheritance, validate with nginx -t, reload through your normal deployment process, and inspect public GET responses afterward.
Do not add Content-Encoding: gzip as a cosmetic header to an unchanged body. The body must contain the matching encoded bytes. If you generate precompressed assets, configure negotiation and correct media types for those variants. Uploading an app.js.br file alone does not make requests for app.js use it.
Keep personalized responses outside this simple static-content recipe. Nginx documents compression risks for TLS responses containing sensitive material. Review such routes separately before enabling a blanket rule.
Debug missing compression through a CDN
If local testing passes and the public response differs, inspect the actual delivery path:
- Confirm the URL is the intended release asset, not an HTML fallback.
- Check its media type and size against the compression policy.
- Inspect the request's accepted encodings and the returned coding.
- Compare the origin response with the public edge response using the provider's supported tools.
- Review cache rules, response transformations, and any
no-transformdirective. - Repeat after a normal cache fill and record the edge/cache status when available.
A CDN may decompress an origin response and recompress it for the visitor. Cloudflare documents separate origin and visitor compression behavior, supported content types, size conditions, and transformation controls. Therefore, origin gzip and public Brotli can both be intentional. Judge each response against that client's request and your delivery contract.
Use the Cache-Control header guide when old variants persist. If a script request returns text/html, follow the JavaScript MIME type error troubleshooting guide before tuning compression. Compressing the wrong response will not repair an asset deployment.
Check transferred and decoded sizes in the browser
Open Chrome DevTools Network, disable cache while DevTools is open, and reload the deployed route. Inspect a large text request's headers and size details. Chrome's Network reference describes showing transferred and uncompressed resource sizes.
Record both values and the cache condition. A cached response with little or no network transfer is not evidence of a particular compression ratio. If a service worker controls the page, isolate that layer as well before comparing a browser result with curl.
For illustration, a stable script with a 300,000-byte decoded body and a 90,000-byte encoded body has a 70% body-size reduction. That calculation excludes headers and is not a measured result for your site. It also says nothing about execution time; use the JavaScript long tasks guide to investigate main-thread work.
Add an HTTP compression release check
For a substantial versioned JavaScript asset that your deployment promises to serve with gzip, save this as check-compression.mjs. It uses Node's HTTPS client to inspect encoded bytes directly and gunzipSync to validate decoding:
import assert from "node:assert/strict";
import https from "node:https";
import { gunzipSync } from "node:zlib";
const target = new URL(process.env.RELEASE_ASSET_URL ?? "");
assert.equal(target.protocol, "https:");
const result = await new Promise((resolve, reject) => {
const req = https.get(target, {
headers: { "Accept-Encoding": "gzip" },
}, (res) => {
const chunks = [];
let bytes = 0;
res.on("data", (chunk) => {
bytes += chunk.length;
if (bytes > 5_000_000) {
res.destroy(new Error("Asset exceeds this check's byte limit"));
return;
}
chunks.push(chunk);
});
res.on("error", reject);
res.on("end", () => resolve({
status: res.statusCode,
headers: res.headers,
body: Buffer.concat(chunks),
}));
});
req.setTimeout(15_000, () => req.destroy(new Error("Request timed out")));
req.on("error", reject);
});
assert.equal(result.status, 200, "Use the final asset URL; no redirects");
assert.match(result.headers["content-type"] ?? "",
/^(text|application)\/javascript(?:\s*;|$)/i);
assert.equal(result.headers["content-encoding"], "gzip");
const vary = String(result.headers.vary ?? "").toLowerCase()
.split(",").map((value) => value.trim());
assert.ok(vary.includes("accept-encoding"), "Missing encoding variance");
const decoded = gunzipSync(result.body, { maxOutputLength: 20_000_000 });
assert.ok(decoded.length > 0, "Empty decoded asset");
assert.ok(result.body.length < decoded.length, "No body-size saving");
console.log({ encodedBytes: result.body.length, decodedBytes: decoded.length });
Run it after deployment with the exact asset URL from that build:
RELEASE_ASSET_URL='https://example.com/assets/app.BUILD_HASH.js' \
node check-compression.mjs
This deliberately checks a gzip contract for one sizeable script; adapt it if your reviewed policy permits another coding or media type. It does not execute the script or check every response. Keep the URL and output with the build revision, then use your website regression testing workflow to investigate failures before changing the assertions.
Check the surrounding release with Slop Audit
Once compression checks pass, run Slop Audit on the deployed public page. Its bounded mobile browser run can provide resource request counts, approximate transferred bytes, lab LCP and CLS, and a long-task blocking-time proxy. Review those findings alongside metadata, crawl controls, runtime errors, and layout evidence, then rescan after a fix.
Slop Audit does not currently negotiate and compare gzip/Brotli variants or validate compression configuration. Transfer measurements can be incomplete when browser timing data is unavailable. Keep the explicit response tests above; use Slop Audit to review the broader launch and catch other readiness issues on the same public route.