From c0bb84bb752e6e205915777131b4a92e4f5f9c95 Mon Sep 17 00:00:00 2001 From: Cam Reeves Date: Mon, 18 May 2026 19:56:34 +1000 Subject: [PATCH 1/2] fix(security): set anti-framing headers at the server level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `X-Frame-Options: DENY` and `Content-Security-Policy: frame-ancestors 'none'` to the default server block. The headers propagate to every location that doesn't declare its own `add_header` directives — i.e. to the HTML surface (front-end SPAs, login screens, static assets) where clickjacking is a concern. Locations with their own `add_header` directives (the `/api/*` CORS preflight handlers, the `/auth/` CORS branch, the `if ($secure_static)` branch under `/`) do not inherit the new headers — those responses are JSON or otherwise not a framing target. Co-Authored-By: Claude Opus 4.7 (1M context) --- config/nginx.conf.template | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/config/nginx.conf.template b/config/nginx.conf.template index 891707e..698d303 100644 --- a/config/nginx.conf.template +++ b/config/nginx.conf.template @@ -50,6 +50,16 @@ http { set $kibana kibana:5601; set $mosquitto mosquitto:9001; + # Defensive clickjacking headers, applied at the server level so they + # propagate to all HTML responses (frontend SPAs, login pages, etc.). + # `frame-ancestors 'none'` (CSP Level 2) is the standards-track form; + # `X-Frame-Options DENY` covers older browsers that don't honour CSP. + # Locations that declare their own `add_header` directives do not + # inherit these — API responses are JSON and not framable, so the + # protection is targeted at the HTML surface that matters. + add_header X-Frame-Options "DENY" always; + add_header Content-Security-Policy "frame-ancestors 'none'" always; + location ~ "^/\.(?!well-known/)" { deny all; } From 19cca58a1be669dbf66ef8b5c3849ab229e55bfe Mon Sep 17 00:00:00 2001 From: "Viv B." Date: Tue, 19 May 2026 11:58:23 +1000 Subject: [PATCH 2/2] fix(nginx): allow SAMEORIGIN --- config/nginx.conf.template | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/nginx.conf.template b/config/nginx.conf.template index 698d303..9e72d58 100644 --- a/config/nginx.conf.template +++ b/config/nginx.conf.template @@ -52,13 +52,13 @@ http { # Defensive clickjacking headers, applied at the server level so they # propagate to all HTML responses (frontend SPAs, login pages, etc.). - # `frame-ancestors 'none'` (CSP Level 2) is the standards-track form; - # `X-Frame-Options DENY` covers older browsers that don't honour CSP. + # `frame-ancestors 'self'` (CSP Level 2) is the standards-track form; + # `X-Frame-Options SAMEORIGIN` covers older browsers that don't honour CSP. # Locations that declare their own `add_header` directives do not # inherit these — API responses are JSON and not framable, so the # protection is targeted at the HTML surface that matters. - add_header X-Frame-Options "DENY" always; - add_header Content-Security-Policy "frame-ancestors 'none'" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header Content-Security-Policy "frame-ancestors 'self'" always; location ~ "^/\.(?!well-known/)" { deny all;