Skip to content

fix(security): stop dropping security headers on static assets and the service worker - #209

Open
fjaeckel wants to merge 1 commit into
mainfrom
security/nginx-security-headers
Open

fix(security): stop dropping security headers on static assets and the service worker#209
fjaeckel wants to merge 1 commit into
mainfrom
security/nginx-security-headers

Conversation

@fjaeckel

Copy link
Copy Markdown
Owner

Summary

Fixes a Medium severity misconfiguration: most responses were served without any security headers.

nginx inherits add_header from an outer block only when the current level defines no add_header of its own — and the always flag does not change this. Several locations set their own Cache-Control and therefore silently discarded the entire server-level set (CSP, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, and on TLS, HSTS):

Location What was lost
location = /sw.js the service worker, served with no CSP
location = /registerSW.js same
location = /env-config.js same
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ every JS, CSS, SVG and font asset — no nosniff, no CSP
location /health same

The same defect was present in the TLS server block generated by docker-entrypoint.sh, where it additionally dropped HSTS.

Changes

  • Headers moved to security-headers.conf, included at the server level and re-included in every location that defines an add_header of its own.
  • Copied into the image via the Dockerfile.
  • HSTS folded into the shared include. User agents ignore HSTS received over plain HTTP (RFC 6797), so emitting it unconditionally is safe — and it avoids a standalone add_header in the TLS block that would re-trigger the same inheritance problem.

Header values are unchanged. This PR is only about where they apply.

Verification

No nginx binary is available in this environment for nginx -t, so instead I ran docker-entrypoint.sh with TLS_DOMAIN set and fake certs in place, and inspected the generated tls.conf — confirming the include lands at the server level and inside /sw.js, /registerSW.js, the static-asset regex and /health.

Worth a quick nginx -t in CI or on a built image before merge, since config syntax can't be fully exercised here.

Related

The Referrer-Policy value (no-referrer-when-downgrade) and the Google Fonts CSP hosts are deliberately left as-is here — those are a separate finding (tokens leaking to third parties via Referer) handled in security/referrer-policy-token-leak. That PR touches the same file, so expect a small conflict depending on merge order.

🤖 Generated with Claude Code


Generated by Claude Code

…e SW

nginx inherits add_header from an outer block ONLY when the current level
defines no add_header of its own -- and the 'always' flag does not change that.
Several locations set their own Cache-Control and therefore silently discarded
the entire server-level security header set:

  location = /sw.js          the service worker, served with NO CSP
  location = /registerSW.js
  location = /env-config.js
  location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$
                             every JS, CSS, SVG and font asset, served with
                             no nosniff and no CSP
  location /health

The same defect was present in the TLS server block generated by
docker-entrypoint.sh, where it additionally dropped HSTS.

The headers now live in security-headers.conf, included at the server level and
re-included in every location that sets an add_header of its own, so the set
survives. HSTS moved into the shared include as well -- user agents ignore it
over plain HTTP (RFC 6797), so emitting it unconditionally is safe and avoids
a standalone add_header in the TLS block that would retrigger the same problem.

Header values are unchanged; this commit is only about where they apply.
Verified by running docker-entrypoint.sh with TLS_DOMAIN set and inspecting the
generated tls.conf.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GukWfyJMY28qv2CJjxFvKF
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.

2 participants