|
|
@@ -0,0 +1,71 @@
|
|
|
+# 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}.
|
|
|
+
|
|
|
+server {
|
|
|
+ listen 80;
|
|
|
+ listen [::]:80;
|
|
|
+ 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;
|
|
|
+
|
|
|
+ # Forwarded headers — trust the upstream reverse proxy.
|
|
|
+ real_ip_header X-Forwarded-For;
|
|
|
+ set_real_ip_from 0.0.0.0/0;
|
|
|
+
|
|
|
+ # Compression.
|
|
|
+ gzip on;
|
|
|
+ gzip_vary on;
|
|
|
+ gzip_min_length 1024;
|
|
|
+ gzip_proxied any;
|
|
|
+ gzip_comp_level 6;
|
|
|
+ gzip_types
|
|
|
+ text/plain
|
|
|
+ text/css
|
|
|
+ text/html
|
|
|
+ text/javascript
|
|
|
+ application/javascript
|
|
|
+ application/json
|
|
|
+ application/xml
|
|
|
+ application/pdf
|
|
|
+ image/svg+xml;
|
|
|
+
|
|
|
+ # Long cache for the PDF (regenerated rarely, content-hash not used).
|
|
|
+ location ~* \.pdf$ {
|
|
|
+ expires 7d;
|
|
|
+ add_header Cache-Control "public, max-age=604800";
|
|
|
+ }
|
|
|
+
|
|
|
+ # Short cache for HTML so content updates land fast.
|
|
|
+ location ~* \.html$ {
|
|
|
+ expires 1h;
|
|
|
+ add_header Cache-Control "public, max-age=3600, must-revalidate";
|
|
|
+ }
|
|
|
+
|
|
|
+ # Logs to stdout/stderr (default in nginx:alpine).
|
|
|
+ access_log /var/log/nginx/access.log;
|
|
|
+ error_log /var/log/nginx/error.log warn;
|
|
|
+
|
|
|
+ # Block access to dotfiles (defense-in-depth — none are shipped anyway).
|
|
|
+ location ~ /\. {
|
|
|
+ deny all;
|
|
|
+ return 404;
|
|
|
+ }
|
|
|
+
|
|
|
+ # Default: serve files, fall back to 404.
|
|
|
+ location / {
|
|
|
+ try_files $uri $uri/ =404;
|
|
|
+ }
|
|
|
+}
|