PLAINFINCH learn by building

File names that change

You will give each version of a file its own name, so browsers can keep it for a year and still get your changes.

Why can't you cache a stylesheet for a year?

The browser caching lesson ended on a trade-off. A long max-age makes the second visit fast, because the phone keeps the file and never asks about it. It also means that when you change shop.css, people who already have it keep the old copy until the time runs out. With a year, that could be a year.

Spoke and Chain chose the safe end: no-cache on everything. Open the Waterfall and switch to Second visit. The stylesheet and the booking script are each checked with the server. Each check is a tiny 304 response, about 0.3 KB, and each one still waits for a round trip before the browser can use its copy.

On this page that costs about 50 ms on a second visit. On a real shop page with a dozen CSS, JavaScript and font files, on a phone with a slower connection, it adds up to hundreds of milliseconds, every visit, for files that almost never change.

What is a fingerprinted file name?

A fingerprint is a short code worked out from the file's contents, usually the first few characters of a hash. shop.3f9a1c.css is the stylesheet whose contents hash to something starting 3f9a1c. Change one colour in the file and the hash changes completely, so the build writes shop.7e2b90.css instead.

That turns the caching problem around. A given URL now always means the same bytes. If the contents change, the URL changes, and a browser that has never seen the new URL has to download it. The old copy in its cache is never used again, because no page asks for it.

Some people call this cache busting, because a new name breaks through any cached copy. The name is the version number, and the server never has to guess whether a file is still current.

<link rel="stylesheet" href="/css/shop.3f9a1c.css">
<script src="/js/booking.8b21d0.js" defer></script>
FigureTwo releases of the Spoke and Chain page. index.html stays no-cache and links shop.3f9a1c.css, cached a year. After an edit it links shop.7e2b90.css, a new URL fetched once; the old name is never requested.Two releases of the Spoke and Chain page. index.html stays no-cache and links shop.3f9a1c.css, cached a year. After an edit it links shop.7e2b90.css, a new URL fetched once; the old name is never requested.
The page is the only file checked every visit. A changed stylesheet gets a new name, so the year-long cache never serves old styles.

Which files get long caching, and which stay no-cache?

Fingerprinted files get the longest lifetime there is: max-age=31536000, one year, which is the conventional maximum. Nothing about them will ever change, so there is no reason for the browser to check.

The HTML page stays no-cache. It is the one file whose name can't change, because people type it, bookmark it and find it in search results. It is also the file that says which fingerprinted names to load. Each visit, the browser checks the page with the server; if you released a new stylesheet, the new page points at the new name and the phone fetches it.

The .htaccess pattern \.[0-9a-f]{6}\.(css|js)$ matches only names with a dot, six hexadecimal characters, a dot and the ending. A plain shop.css doesn't match, so if someone links the unfingerprinted file by mistake, it falls back to no-cache and is never stuck.

Header set Cache-Control "no-cache"

<FilesMatch "\.[0-9a-f]{6}\.(css|js)$">
  Header set Cache-Control "max-age=31536000, immutable"
</FilesMatch>

What does immutable add?

immutable promises that the file at this URL will never change during its lifetime. Without it, some browsers still check a fresh file when the visitor presses reload, because a reload looks like a request for the latest version.

Firefox and Safari skip that check when they see immutable. Chrome already avoids most revalidation of page resources on an ordinary reload, so it gains less, but the value does no harm there. Only use it on fingerprinted files. On a fixed name like shop.css it would make the stuck-file problem worse.

What changes in the waterfall after the switch?

On the first visit, nothing much: the fingerprinted files are the same size as the old ones, about 17 KB and 29 KB after Brotli. The first visit is still about 136 KB with the largest paint at about 1.6 seconds.

On the second visit, both files are tagged from cache and finish in a couple of milliseconds. The largest paint moves from about 1,026 ms to about 973 ms. The workshop photo is still checked, because the FilesMatch pattern only covers CSS and JS; you could fingerprint images too and widen the pattern.

The simulator always fetches the HTML page again on a second visit, whatever its header says. A real browser would obey a long max-age on the page as well, which is exactly why the page must keep no-cache: a cached page keeps linking to last month's file names.

How do the fingerprinted files get made?

By hand, it is a rename: copy shop.css to shop.3f9a1c.css, change the link in every page that uses it, and upload both. That works for a tiny site, but it is easy to get wrong, as the Break It shows: a link that points at a name that was never uploaded is a 404 and an unstyled page.

Build tools do it automatically. Almost every bundler and static site generator can add a content hash to output file names and rewrite the HTML to match. Look for a setting called hashing, fingerprinting or asset versioning. Once it is on, you edit shop.css as usual and the tool takes care of the name.

Keep the previous fingerprinted files on the server for a few weeks after each release. Someone may have the old page open in a tab, or a search engine may have cached a copy. Deleting old files the moment you deploy turns those visits into broken pages.

Isn't a query string like ?v=2 enough?

Adding ?v=2 to the link, as in shop.css?v=2, also makes a new URL, and browsers cache it separately. It is a common shortcut and it does work in browsers.

It has two weaknesses. The number is updated by hand, so it is easy to change the file and forget the number, which brings back the stuck-file problem. And some caching proxies and CDNs are set to ignore query strings, so they may hand out the old file anyway. A fingerprint in the file name avoids both, because the name comes from the contents.

What should you check when AI sets up caching?

AI assistants often suggest max-age=31536000, immutable for every CSS and JS file on the site, without checking whether the names are fingerprinted. On a hand-edited site with fixed names, that freezes the old styles in returning visitors' browsers for a year.

They also sometimes cache the HTML for a day or more to improve a test score, or suggest deleting old built files on every deploy. Check three things in any proposal: the long lifetime only applies to names that change with their contents, the HTML stays no-cache, and old files stay available for a while.

Order inside .htaccess does not decide which value wins. Apache applies FilesMatch sections after the plain directives wherever you put them, and the simulator does the same. Keeping the block below the plain no-cache line is still easier for anyone reading the file.

Field notes

  • A long cache lifetime is fast but can leave visitors with old styles.
  • A fingerprinted name changes whenever the file's contents change.
  • Fingerprinted files get max-age=31536000, immutable; the HTML stays no-cache.
  • immutable tells browsers not to recheck the file, even on reload.

Easy to get wrong

A year-long lifetime on a fixed name means returning visitors keep the old file for a year after you change it. Only fingerprinted names are safe to cache that long.

The page lists which file names to load. If it is cached, visitors keep loading the old names even after you release new ones.

Pages already open in tabs, or cached by others, still point at the old names. Removing the files breaks those pages.

Questions people ask

How long should the fingerprint be?

Six to eight hexadecimal characters is plenty for a site's own files. The chance of two different versions of the same file sharing a fingerprint that long is tiny.

Can I fingerprint images and fonts?

Yes, and build tools usually do. Widen the FilesMatch pattern to include their endings. Images are often the largest files on a page, so caching them for a year saves the most.

Does this help first-time visitors?

No. A first visit downloads every file once whatever the headers say. Fingerprinting makes later visits fast without risking stale files.

What if my host serves files through a CDN?

The same headers work: the CDN keeps fingerprinted files for a year as well. When you release, the new names are fetched from your server the first time someone asks for them, so there is nothing to purge.

Keep going