server { listen 80; server_name _; root /var/www/html/public; index index.php; # Redirect HTTP to HTTPS (behind load balancer / reverse proxy) if ($http_x_forwarded_proto = "http") { return 301 https://$host$request_uri; } # Allow file uploads up to 25MB (mercuriale import, BL photos) client_max_body_size 25M; # Security headers add_header X-Frame-Options "DENY" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "camera=(self), geolocation=(), microphone=()" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # CSP : strict mais compatible PWA (blob: pour la caméra future) add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' data:; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; img-src 'self' blob: data:; font-src 'self' https://cdnjs.cloudflare.com; connect-src 'self'; manifest-src 'self'; worker-src 'self';" always; # Le Service Worker ne doit JAMAIS être mis en cache par le navigateur location = /sw.js { add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Service-Worker-Allowed "/"; add_header X-Content-Type-Options "nosniff" always; add_header Content-Type "application/javascript"; } # Manifest : cache court location = /manifest.json { add_header Cache-Control "public, max-age=3600"; add_header Content-Type "application/manifest+json"; } # Gzip compression gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/javascript application/json application/xml; # Handle static files (sw.js is excluded — handled by its own location block above) location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|svg|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; try_files $uri =404; } # Handle PHP location / { try_files $uri /index.php$is_args$args; } location ~ ^/index\.php(/|$) { fastcgi_pass unix:/var/run/php-fpm.sock; fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; fastcgi_param DOCUMENT_ROOT $realpath_root; fastcgi_buffer_size 128k; fastcgi_buffers 4 256k; fastcgi_busy_buffers_size 256k; internal; } # Deny access to .php files in other locations location ~ \.php$ { return 404; } # Deny access to hidden files location ~ /\. { deny all; } # Health check endpoint location /health { access_log off; return 200 "OK"; add_header Content-Type text/plain; } }