| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778 |
- # 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";
- }
- # 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";
- access_log off;
- }
- # 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;
- }
- }
|