Back to Colophon
Back to Colophon

Spent hours convinced my images weren't caching across View Transitions navigations. Server headers were perfect. Cloudflare config was perfect. The problem was DevTools "Disable cache" being checked.

#performance#debugging

The setup#

I’d just finished rolling out LQIP placeholders across the whole site. Blur previews for every content image, base64-encoded inline, the works. Felt good about it. Then I opened DevTools to admire my handiwork.

What I saw#

Navigating from /about to / and back, every single AVIF image re-downloaded at full size. Same URLs, same bytes, all returning 200. The LQIP base64 placeholders showed “(memory cache)” at 0ms, but every real image was hitting the network fresh.

I went deep. Checked the _headers file. Checked curl -I against production. Checked if Astro’s ClientRouter was doing something weird with fetch() cache modes. Checked if <picture> elements with multiple <source> children triggered re-evaluation during DOM swaps. Read through View Transitions docs.

The actual problem#

DevTools “Disable cache” was checked. It’s almost always checked. I check it on purpose because I’m usually working on the site and don’t want stale assets.

With that box checked, Chrome adds Cache-Control: no-cache to every HTTP request, completely bypassing disk cache. The base64 LQIP images were immune because they’re inline data URIs, not HTTP resources. They can’t be “re-fetched.” That’s why they showed memory cache while everything else re-downloaded.

The clue I missed#

The LQIP placeholders showing “(memory cache)” while real images showed full byte sizes was the tell from the start. If there were an actual caching misconfiguration on the server, the base64 images wouldn’t behave differently. They’d all be broken, or none would. The split behavior pointed directly at something intercepting HTTP requests but not inline data. That’s exactly what “Disable cache” does.

What was actually fine#

cache-control: public, max-age=31536000, immutable
cf-cache-status: HIT

Server headers were correct the whole time. Cloudflare’s _headers file was applying immutable to /_astro/* and /images/*. The edge cache was warm. Everything was working exactly as configured.

The lesson#

Before debugging caching, uncheck “Disable cache.” It’s the console.log of network debugging: check the obvious thing first.

Spent hours convinced my images weren't caching across View Transitions navigations. Server headers were perfect. Cloudflare config was perfect. The problem was DevTools "Disable cache" being checked.