From 73ad7af20da0f44e77814c81054f7f8238ebef56 Mon Sep 17 00:00:00 2001 From: liyuanyang Date: Tue, 1 Sep 2026 13:57:49 +0800 Subject: [PATCH] fix(web): nginx drops security headers on /assets/ and duplicates Referrer-Policy on proxied API responses - /assets/ location now repeats the security header trio explicitly because its own Cache-Control add_header breaks server-level inheritance - /v1/ location suppresses inherited security headers with empty-value add_header entries so only the API's own headers reach the client - Header ownership rule documented in the config template Refs #135 --- CHANGELOG.md | 10 ++++++++++ web/nginx/default.conf.template | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 583287f..5594510 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ The project publishes 0.x prerelease versions; a stable release line is not yet ## [Unreleased] +### Fixed + +- nginx no longer drops security headers on `/assets/` responses or duplicates + `Referrer-Policy` on proxied API responses. The `/assets/` location repeats + the security header trio explicitly (its own `Cache-Control` `add_header` + breaks server-level inheritance), and the `/v1/` location suppresses the + inherited trio with empty-value `add_header` entries so only the API's own + security headers reach the client. A header ownership rule is documented in + the config template. + ### Changed - Migrate GitHub repository, Release, issue, badge, and raw-content coordinates diff --git a/web/nginx/default.conf.template b/web/nginx/default.conf.template index 48b4382..c2d500a 100644 --- a/web/nginx/default.conf.template +++ b/web/nginx/default.conf.template @@ -7,6 +7,15 @@ server { index index.html; client_max_body_size ${MEM_MAX_BODY_SIZE}; + # Header ownership rule: + # - The API (memd) is authoritative for /v1/ responses. It sets its own + # security headers and is reachable without nginx in bare-metal or + # alternative-ingress deployments. nginx suppresses the inherited + # server-level duplicates below so only the API's values reach the + # client (nginx add_header appends; empty-value entries are skipped). + # - nginx owns security headers for /assets/ and the SPA document (/). + # Because add_header inheritance is suppressed when a nested block + # defines any add_header of its own, /assets/ repeats the trio. add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "same-origin" always; add_header X-Frame-Options "DENY" always; @@ -18,6 +27,13 @@ server { } location /v1/ { + # Suppress inherited security headers: the API sets its own values. + # An empty add_header value prevents nginx from emitting the header, + # leaving the upstream response untouched. + add_header X-Content-Type-Options "" always; + add_header Referrer-Policy "" always; + add_header X-Frame-Options "" always; + proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; @@ -32,6 +48,11 @@ server { location /assets/ { try_files $uri =404; expires 1y; + # This block defines its own add_header, which breaks inheritance from + # the server level. Repeat the security header trio explicitly. + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "same-origin" always; + add_header X-Frame-Options "DENY" always; add_header Cache-Control "public, immutable"; }