Compression and cache audit

Two response headers decide how many bytes travel on a first visit and how many travel all over again on the next. This audit reads them on your page and on up to fourteen of its own files.

Or try mozilla.org, wordpress.org

A short exchange in the headers

With every request, a browser announces the compression formats it knows how to unpack:

Accept-Encoding: gzip, deflate, br, zstd

A server with compression turned on picks one, shrinks the file and labels the response Content-Encoding: br (Brotli) or gzip. HTML, CSS and JavaScript are repetitive text and typically lose 70 to 85% of their size. JPEG, PNG, WebP, AVIF and WOFF2 files are already compressed by their own format, so sending them as they are is the right thing to do.

Caching is the other half. Cache-Control: max-age=31536000 lets the browser reuse a file for a year without asking. Once the lifetime runs out, an ETag or Last-Modified header lets the browser ask a cheap question, “has this changed?”, and the server answers 304 Not Modified with no body.

Which files are audited

We start with the page itself, after redirects, then take files from the same registered domain found in its HTML: up to four stylesheets, five scripts, three images and two fonts. Places left unused go to the remaining files, for fifteen in all, and fonts referenced inside your stylesheets are added when there’s room. Each file is requested the way a browser would, offering Brotli and gzip, over HTTP/2 when the server supports it.

Files on other domains, such as Google Fonts or an analytics script, are left out. Their headers aren’t yours to set.

How each column is judged

Compression
Green when a text file arrives with a Content-Encoding. Red when the page, or a text file above 20 KB, arrives raw. Orange for smaller raw files, and we don’t hold anything against files under 1 KB. For each raw file, the audit gzips the content itself and shows the size you’d get. Images and WOFF2 fonts read “not needed”.
Cache
The lifetime comes from max-age, or from Expires when there’s no max-age. A stylesheet, script, image or font is marked when it has no lifetime or one shorter than seven days. no-store and no-cache count as zero. The HTML page is shown for information only, with a caution if it may be kept more than an hour, since returning visitors could then be looking at an outdated version.
Protocol
Passes with HTTP/2, which carries all files over one connection in parallel. HTTP/3 is detected through the alt-svc header, where a server announces h3.
Savings
Transferred bytes against unpacked bytes for the whole sample.

A site that uses gzip everywhere gets a note about Brotli, which makes text files roughly 15 to 20% smaller. Take it as an improvement to plan. Nothing is wrong without it.

Setting it up safely

When something needs fixing, the result includes ready-made blocks for Apache and nginx. Behind a CDN such as Cloudflare, compression happens at the edge and cache lifetimes are a dashboard setting. On WordPress, caching plugins write the same rules for you.

For the lifetime, one rule decides everything. A long cache is only safe if the file’s address changes whenever its content does. Build tools produce names like app.3f9a1c.js, and WordPress appends ?ver=6.8. Files like those can carry:

Cache-Control: public, max-age=31536000, immutable

Files with a fixed name are better off at a week to a month. For HTML, Cache-Control: no-cache keeps a copy and checks it with the server before each use.

Despite its name, no-cache means “revalidate before using”. The directive that forbids keeping a copy is no-store, and it belongs on pages with personal data.

To confirm a change from a terminal:

curl -sI -H "Accept-Encoding: br, gzip" https://example.com/ | grep -iE "content-encoding|cache-control"

What the audit leaves out

It’s a sample of fifteen files from one page, so a problem that only affects another template can slip through. It doesn’t offer Zstandard. It reads max-age and ignores s-maxage, which only concerns shared caches. And it skips files that your own CDN serves from a different domain. Each file is downloaded up to 3 MB.

Questions people ask

How do I check if gzip or Brotli is enabled on my site?

Request a page with an Accept-Encoding header and look for Content-Encoding in the response. This audit does that for the page and its main files, and the curl command above does it for one address.

Is Brotli better than gzip?

For text, yes. Files come out about 15 to 20% smaller, and every current browser accepts it over HTTPS. Gzip is still a good baseline and the fallback for older clients. Servers usually turn on both.

What Cache-Control should I use for CSS and JavaScript?

If the file name or query string changes with each release, public, max-age=31536000, immutable. If the name never changes, go for a shorter lifetime such as a week, or visitors will be stuck with the old version after an update.

Should images be gzipped?

No. JPEG, PNG, WebP and AVIF are already compressed, and gzip would burn processor time to save almost nothing. SVG is the exception, since it’s text and compresses very well.

Why are some of my files missing from the table?

We only check files on the same registered domain as the page, fifteen at most. Files served from another domain, and files that scripts load later, aren’t in the sample.

Do I need HTTP/3?

HTTP/2 is the step that matters, and nearly every host offers it. HTTP/3 mostly helps on unstable mobile connections, and the simplest way to get it is a CDN that turns it on by default.