From 534cf4d3b26fd47f180de41c8663b7e99254d9b4 Mon Sep 17 00:00:00 2001 From: ssavutu Date: Sat, 1 Aug 2026 14:49:28 -0400 Subject: [PATCH] fix(nginx): stop caching 404s for 30 days on legacy media `add_header ... always` applies the header to error responses too, so every 404 from the /wp-content/ block went out with `Cache-Control: public, max-age=2592000, immutable`. Cloudflare took that at face value and pinned "this file does not exist" at the edge for a month. The consequence is that any file added to the corpus AFTER something first requested it stays invisible for 30 days -- on disk, served correctly by this nginx, still 404 to every visitor. It reads as a failed copy rather than a cache hit, which is what makes it expensive to diagnose. Hit while migrating wp-content/uploads/newsletter/ to CephFS: the files landed, the origin served them, and the edge kept replaying a 404 cached minutes earlier. Confirmed by cf-cache-status: HIT with a stale age, while the same path with a cache-busting query string returned 200. Drop `always` from the Cache-Control add_header only. Without it the header applies to 2xx/3xx, and misses fall back to Cloudflare's short default 404 TTL, so a later-added file self-heals. X-Content-Type-Options and Content-Security-Policy keep `always` on purpose -- those must apply to error responses. This does not purge anything already cached; existing poisoned entries age out on their own. Co-Authored-By: Claude Opus 5 --- deploy/nginx/triangle-cms.conf | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/deploy/nginx/triangle-cms.conf b/deploy/nginx/triangle-cms.conf index 4bd4bcd..9273794 100644 --- a/deploy/nginx/triangle-cms.conf +++ b/deploy/nginx/triangle-cms.conf @@ -75,7 +75,16 @@ server { # Filenames encode the exact size, so files are immutable. Set this only # via add_header -- `expires` would emit a second, weaker Cache-Control # and CDNs disagree about which duplicate wins. - add_header Cache-Control "public, max-age=2592000, immutable" always; + # + # Deliberately NOT `always`: that flag also stamps 30-day-immutable onto + # 404s, and Cloudflare then pins "this file does not exist" at the edge + # for a month. Any file added to the corpus after something first + # requested it stays invisible until the TTL expires or someone purges -- + # a migrated image that is provably on disk and served fine by this nginx + # still 404s publicly, which reads as a failed copy rather than a cache + # hit. Without the flag add_header applies only to 2xx/3xx, so misses + # fall back to Cloudflare's short default 404 TTL and self-heal. + add_header Cache-Control "public, max-age=2592000, immutable"; } # Never serve dotfiles (keep ACME http-01 working if it is ever added here).