server { listen 80; server_name localhost; root /var/www/html/public; # Allow file uploads up to 25MB client_max_body_size 25M; location / { try_files $uri /index.php$is_args$args; } location ~ ^/index\.php(/|$) { fastcgi_pass php:9000; fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; fastcgi_param DOCUMENT_ROOT $realpath_root; # Prevents URIs that include the front controller internal; } location ~ \.php$ { return 404; } # Security headers add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "DENY" 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; # 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"; } # Deny access to hidden files location ~ /\. { deny all; } # Deny access to sensitive files location ~* \.(env|htaccess|git)$ { deny all; } error_log /var/log/nginx/project_error.log; access_log /var/log/nginx/project_access.log; }