fix(nginx): never cache index.html so clients pick up new bundles - #255
fix(nginx): never cache index.html so clients pick up new bundles#255rajivsinclair wants to merge 1 commit into
Conversation
index.html had no Cache-Control header, so browsers applied heuristic caching and could serve a stale entrypoint pointing at an old, deleted content-hashed bundle. This surfaced during the Supabase key rotation: clients on a cached index.html kept loading the pre-rotation bundle with the now-disabled legacy anon key and got 'Legacy API keys are disabled'. Add an exact-match location for /index.html with no-store so the HTML entrypoint is always revalidated, while hashed assets keep their 1y immutable cache. Standard SPA cache strategy.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughThis PR modifies nginx.conf to add an explicit cache control rule for the SPA entrypoint (index.html). A new location block is inserted before the static asset caching rule, setting ChangesSPA Entrypoint Cache Control
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~5 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Preview Deployment Ready!URL: https://pr-255-verdad-frontend.fly.dev Preview auto-updates on push. Destroyed when PR closes. |
There was a problem hiding this comment.
Code Review
This pull request updates the Nginx configuration to prevent caching of the SPA entrypoint (index.html) by adding a specific location block with a Cache-Control header. The reviewer suggested appending the 'always' parameter to the add_header directive and adding 'no-cache' to ensure the header is consistently sent across all HTTP status codes (such as 404s) and to maximize compatibility.
| # caching and may skip revalidation. no-store forces a fresh fetch every | ||
| # load so clients always pick up the current bundle immediately on deploy. | ||
| location = /index.html { | ||
| add_header Cache-Control "no-store, must-revalidate"; |
There was a problem hiding this comment.
By default, Nginx's add_header directive only applies to successful responses (like 200 or 301). When a request triggers a 404 (such as a missing static asset falling back to /index.html via error_page 404), Nginx serves index.html with a 404 status code but omits the Cache-Control header. This can cause browsers to cache the 404 response or cache the HTML under a static asset's URL, leading to JavaScript parsing errors. Appending the 'always' parameter ensures the header is sent for all status codes, and adding 'no-cache' ensures maximum compatibility.
add_header Cache-Control "no-store, no-cache, must-revalidate" always;
What
Add an exact-match
location = /index.htmlblock innginx.confthat setsCache-Control: no-store, must-revalidate, so the SPA entrypoint is never cached by browsers. Hashed assets (*.js,*.css, etc.) keep their existing1y immutablecache.Why
index.htmlwas served with noCache-Controlheader — onlyLast-Modified/ETag. Browsers then apply heuristic caching (RFC 7234 §4.2.2) and can serve a staleindex.htmlwithout revalidating. Becauseindex.htmlreferences content-hashed bundles (index-<hash>.js), a stale entrypoint points at an old bundle that the new build no longer serves.This bit us during the Supabase API key rotation (compromised
service_rolekey): the pre-rotation bundle had the legacyanonJWT baked in. After we disabled the legacy keys, clients still loading a cachedindex.html→ old bundle kept sending the dead key and hit:The origin and the freshly-built bundle (
index-sN9AmqBz.js) were already correct — the only stale layer was browser-cachedindex.html.Fix
Standard SPA caching strategy: immutable hashed assets + never-cached HTML entrypoint. Every future deploy then takes effect immediately for all clients.
Notes / things to check
locationprecedence: exact match (= /index.html) wins over the prefixlocation /and the asset regex, so the header applies on direct hits.try_files ... /index.htmlanderror_page 404 /index.htmlperform internal redirects that re-matchlocation = /index.html, so fallback-served HTML is alsono-store. Worth a sanity check on a preview deploy.Verification after deploy
Context: follow-up to today's Supabase service_role key rotation incident.
Summary by CodeRabbit