Browser Caching: Long Cache Lifetimes for Static Files
When a browser downloads a file, the response’s Cache-Control header tells it how long it may
reuse that copy without asking the server again. With a long lifetime, a returning visitor loads
your stylesheets, scripts, fonts and logo straight from disk. With none, the browser downloads or
revalidates them on every visit.
Why it matters
Repeat visits and multi-page sessions are where caching pays off: a visitor who opens three pages shouldn’t download the same 300 KB of CSS and JavaScript three times. It also reduces load on your server and bandwidth costs. First visits don’t benefit, which is why this is a low-severity check.
The key headers
Cache-Control: max-age=<seconds>sets how long the copy counts as fresh. During that time the browser uses it without any request.immutabletells the browser the file will never change at that URL, so it doesn’t need to revalidate it, even when the visitor reloads the page.ETagandLast-Modifiedlet the browser revalidate an expired copy cheaply: it asks “has this changed?” and the server answers304 Not Modifiedwith no body if it hasn’t.
Versioned files: cache for a year
The safe way to cache static files for a long time is to change their URL whenever their content
changes, for example app.3f9a1c.js or style.css?ver=2.4.1. Build tools add a content hash
automatically; WordPress adds ?ver= to enqueued assets. A new deployment then produces new URLs,
and the old cached copies are simply never requested again. These files can be served with:
Cache-Control: public, max-age=31536000, immutable
That is one year, the common maximum.
Why HTML is different
HTML pages keep the same URL when their content changes, so a long max-age would leave visitors
with stale pages and stale references to old asset files. Serve HTML with Cache-Control: no-cache
(the browser may store it but must revalidate before each use) or a short max-age, and keep the
long lifetimes for the versioned files it references.
How to fix
Nginx
location ~* \.(?:css|js|woff2|avif|webp|jpg|jpeg|png|gif|svg|ico)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
Only use this if those files are versioned. If your theme or app overwrites files at the same URL,
use a shorter lifetime such as a week (max-age=604800) without immutable.
Apache
<IfModule mod_headers.c>
<FilesMatch "\.(css|js|woff2|avif|webp|jpe?g|png|gif|svg|ico)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
</IfModule>
Cloudflare
The Browser Cache TTL setting and Cache Rules can change the lifetime browsers receive. If your origin already sends the right headers, choose “Respect Existing Headers”; otherwise set the lifetime for static files with a Cache Rule. Cloudflare’s edge cache is separate: it speeds up delivery from the CDN but doesn’t change what the visitor’s browser keeps.
WordPress
Many caching and optimisation plugins can add browser caching headers for static files. On shared
hosting, the Apache snippet above usually works in .htaccess. Third-party files, such as
analytics scripts, are cached according to their provider’s headers, which you can’t change.
How PerfoMetrix checks this
We read Lighthouse’s use efficient cache lifetimes audit (cache-insight) from the PageSpeed
Insights run we already make (mobile, simulated device). If Lighthouse rates it 0.9 or higher, or
it doesn’t apply to the page, the check passes; otherwise it needs attention. We show Lighthouse’s
estimated savings and the first five files with short cache lifetimes.
This check is shown but doesn’t count toward the performance score, like the other Lighthouse diagnostics: the metrics they explain (LCP, TBT, CLS, FCP) are scored already. No extra requests are made to your site.