Skip to content

fix(nginx): cache hashed assets as immutable, revalidate the rest - #107

Merged
oto-macenauer-absa merged 1 commit into
masterfrom
fix/cache-hashed-assets
Sep 24, 2026
Merged

oto-macenauer-absa merged 1 commit into
masterfrom
fix/cache-hashed-assets

Conversation

@oto-macenauer-absa

Copy link
Copy Markdown
Collaborator

Closes #66.

Both prefix locations are ^~, which stops nginx evaluating regex locations, so the location ~* \.(css|js|…)$ long-cache block never ran in production. Hashed _astro/ files went out with Cache-Control: no-transform only (under /knowledge-base/) or no Cache-Control at all (under /__wf/knowledge-base/).

Changes

A map $request_uri $kb_cache_control picks the policy per asset class, and both prefix locations add_header Cache-Control $kb_cache_control (each re-including kb-headers.conf):

class Cache-Control
_astro/* (Astro-hashed), {slug}/_kb-inline/<16 hex>.js (content-addressed hoisted scripts) public, max-age=31536000, immutable, no-transform
everything else — HTML, style.css, sub-app assets no-cache, no-transform
  • Keyed on $request_uri: by the time add_header runs, $uri has lost the prefix to the rewrite.
  • Differs from the issue on HTML and sub-app assets: no-cache (revalidate via ETag → 304) rather than unchanged/heuristic. Sub-app assets keep whatever names their generator gave them (docs/style.css), so they have the same stale-for-a-while-after-deploy risk the issue raised for style.css.
  • No always: a 404 under _astro/ must not be cached for a year.
  • The unreachable regex location is removed; no-transform still rides on every response.

Tests

  • nginx-config.spec.js: both ^~ locations use the map, without always; no regex location sets a policy; the map and the Express mirror agree on every pattern and value.
  • container.spec.js (real nginx): each class under both prefixes, 404 not immutable, ETag → 304.
  • standalone.spec.js + tests/fragment-server.mjs: the mirror serves the same map.

Verification

  • npm run test:container: 36 passed (fresh image; a stale container on :8099 removed first)
  • playwright.config.ci.js: 23 passed
  • npm test: 396 passed

🤖 Generated with Claude Code

Every asset is served from a ^~ prefix location, and ^~ skips regex
locations, so the `location ~* \.(css|js|…)$` long-cache block never ran:
hashed _astro/ files went out with only `no-transform` under
/knowledge-base/ and no Cache-Control at all under /__wf/knowledge-base/.

A map on $request_uri now picks the policy per asset class, applied in both
prefix locations (each re-including kb-headers.conf):

- _astro/ and the content-addressed _kb-inline/ scripts:
  public, max-age=31536000, immutable, no-transform
- everything else (HTML, style.css, sub-app assets at stable names):
  no-cache, no-transform — revalidated by ETag, 304 when unchanged

Not `always`, so a 404 under _astro/ is never cached for a year. The dead
regex location is removed. The Express mirror serves the same map;
nginx-config.spec asserts the two agree, container.spec asserts every class
against the real image.

Closes #66

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@oto-macenauer-absa
oto-macenauer-absa merged commit 8312482 into master Sep 24, 2026
8 checks passed
@oto-macenauer-absa
oto-macenauer-absa deleted the fix/cache-hashed-assets branch September 24, 2026 15:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hashed assets are never cached: the immutable location is unreachable for prefixed paths

1 participant