Remember your 404s until the next deploy
Most caching advice is about remembering things that exist. This one is about remembering things that don't. If you serve static files from object storage or a bucket behind a worker, every request for a missing file st
Most caching advice is about remembering things that exist. This one is about remembering things that don't.
If you serve static files from object storage or a bucket behind a worker, every request for a missing file still costs you a lookup. Browsers ask for favicon.ico at the root even if you never had one. Bots probe for paths that were never there. Old links point at files that got renamed. Each of those is a round trip to storage that ends in the same answer: not here.
I added a small negative cache for this to a thing I run, and the rule that made it safe was simple: a 404 is only true until the next deploy.
Key it on the deploy
The set of files can only change when you deploy. So if you include the deploy ID in the cache key, you never have to invalidate anything. A new deploy means new keys, and the old entries just stop being read.
const key = `404:${deployId}:${url.pathname}`;
if (await cache.get(key)) {
return new Response("Not found", { status: 404 });
}
const file = await bucket.get(objectPath);
if (!file) {
await cache.put(key, "1");
return new Response("Not found", { status: 404 });
}
That's most of it. Use whatever cache you already have. The important part is the key, not the store.
Why not just a TTL?
A TTL works, but it forces you to pick a number. Too long and a file you just deployed stays "missing" for a while. Too short and you get little benefit. Tying it to the deploy removes the guess: the answer is valid for exactly as long as the files are the same.
Things to watch
- Only cache real misses. If storage errors or times out, that is not a 404. Don't remember it.
-
Normalize the path first. Otherwise
/a/../band/bbecome two entries, and someone can fill your cache with junk paths. - Bound it if you can. Bots generate a lot of unique paths. An expiry on top of the deploy key, or a cache that evicts on its own, keeps that in check.
- Make sure the deploy ID is actually per deploy. If it's the same across releases, you've built a cache that never forgets.
Is it worth it?
It depends on how much junk traffic you get. It's a few lines, nothing to configure, and nothing to invalidate by hand. That's about the right size for a fix like this: small enough that it's hard to get wrong, and when it works nobody notices.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.