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.
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: HITServer 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.