fix(security): unprivileged nginx + security headers on every location
- base image -> nginxinc/nginx-unprivileged:1.28-alpine, digest-pinned (BREAKING for the docker path: container port 80 -> 8080; compose mapping/healthcheck updated in the same change, cap_add dropped) - nginx add_header inheritance fix: shared snippets file re-included in every location that sets Cache-Control -- previously ALL security headers were dropped on real responses (verified live before/after) - server_tokens off; set_real_ip_from restricted to 127.0.0.1 - expires directives removed (duplicated Cache-Control); gzip_types text/html redundancy removed (nginx -t warn)
This commit is contained in:
+18
-21
@@ -1,29 +1,27 @@
|
||||
# nginx server block for bchanot.fr static site.
|
||||
# Container listens on port 80; host port is configured via docker-compose
|
||||
# (PORT env var). A host-level reverse proxy (nginx, Traefik, Caddy) should
|
||||
# terminate TLS and proxy_pass to http://127.0.0.1:${PORT}.
|
||||
# Container (nginx-unprivileged) listens on 8080; host port is configured via
|
||||
# docker-compose (PORT env var). A host-level reverse proxy (nginx, Traefik,
|
||||
# Caddy) should terminate TLS and proxy_pass to http://127.0.0.1:${PORT}.
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
listen 8080;
|
||||
listen [::]:8080;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Security headers. HSTS is intentionally NOT set here — leave it to the
|
||||
# outer reverse proxy that terminates TLS, otherwise it may be sent over
|
||||
# plain HTTP between proxy and container.
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()" always;
|
||||
# CSP: inline CSS + JS are allowed (project convention), fonts from Google.
|
||||
add_header Content-Security-Policy "default-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; script-src 'self' 'unsafe-inline'; img-src 'self' data:; base-uri 'self'; form-action 'self'; frame-ancestors 'self'" always;
|
||||
# Don't advertise the nginx version.
|
||||
server_tokens off;
|
||||
|
||||
# Forwarded headers — trust the upstream reverse proxy.
|
||||
# Security headers — shared snippet. Re-included in every location that
|
||||
# sets its own add_header (inheritance is all-or-nothing in nginx).
|
||||
include /etc/nginx/snippets/security-headers.conf;
|
||||
|
||||
# Forwarded headers — trust only the local reverse proxy (the container
|
||||
# port is bound to 127.0.0.1 in docker-compose).
|
||||
real_ip_header X-Forwarded-For;
|
||||
set_real_ip_from 0.0.0.0/0;
|
||||
set_real_ip_from 127.0.0.1;
|
||||
|
||||
# Compression.
|
||||
gzip on;
|
||||
@@ -34,7 +32,6 @@ server {
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
text/html
|
||||
text/javascript
|
||||
application/javascript
|
||||
application/json
|
||||
@@ -44,24 +41,24 @@ server {
|
||||
|
||||
# Long cache for the PDF (regenerated rarely, content-hash not used).
|
||||
location ~* \.pdf$ {
|
||||
expires 7d;
|
||||
add_header Cache-Control "public, max-age=604800";
|
||||
include /etc/nginx/snippets/security-headers.conf;
|
||||
}
|
||||
|
||||
# Short cache for HTML so content updates land fast.
|
||||
location ~* \.html$ {
|
||||
expires 1h;
|
||||
add_header Cache-Control "public, max-age=3600, must-revalidate";
|
||||
include /etc/nginx/snippets/security-headers.conf;
|
||||
}
|
||||
|
||||
# Long cache for favicon + image assets (rarely change).
|
||||
location ~* \.(?:ico|svg|png|jpg|jpeg|gif|webp)$ {
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, max-age=2592000, immutable";
|
||||
include /etc/nginx/snippets/security-headers.conf;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Logs to stdout/stderr (default in nginx:alpine).
|
||||
# Logs to stdout/stderr (default in nginx images).
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user