# ============================================================================= # deploy/nginx.conf — Configuration Nginx À L'INTÉRIEUR DU CONTENEUR # ----------------------------------------------------------------------------- # DIFFÉRENCE MAJEURE avec nginx.conf.example (à la racine du projet) : # # Ce fichier ne gère NI TLS, NI redirection HTTP vers HTTPS, NI redirection # du domaine nu vers www. Traefik, le reverse proxy de Coolify, s'en charge # en amont. # # Dupliquer ces redirections ici provoquerait une boucle infinie : Traefik # transmet la requête en HTTP clair au conteneur, qui la redirigerait vers # HTTPS, ce qui repasserait par Traefik, et ainsi de suite. # # Le conteneur écoute donc en HTTP simple sur le port 80, en interne. # nginx.conf.example reste utile si le site est un jour servi sans Coolify. # ============================================================================= server { listen 80; listen [::]:80; server_name _; root /usr/share/nginx/html; index index.html; charset utf-8; # ------------------------------------------------------------------------- # JOURNALISATION # Sortie standard : Coolify récupère les journaux du conteneur. # Écrire dans un fichier ferait grossir la couche du conteneur sans profit. # ------------------------------------------------------------------------- access_log /dev/stdout; error_log /dev/stderr warn; # Masque le numéro de version dans les en-têtes et les pages d'erreur server_tokens off; # ========================================================================= # EN-TÊTES DE SÉCURITÉ # ------------------------------------------------------------------------- # HSTS est volontairement ABSENT ici : c'est Traefik qui termine le TLS, # c'est donc à lui de l'émettre (voir DEPLOIEMENT-COOLIFY.md). L'émettre # depuis le conteneur fonctionnerait, mais placerait un réglage # irréversible dans une couche qu'on redéploie souvent. # ========================================================================= 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:; connect-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; object-src 'none'; upgrade-insecure-requests" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=(), interest-cohort=()" always; add_header X-Frame-Options "DENY" always; # ========================================================================= # COMPRESSION # ========================================================================= gzip on; gzip_vary on; # « Vary: Accept-Encoding » — sans lui, un proxy # peut servir du gzip à un client incapable de # le lire. gzip_comp_level 6; gzip_min_length 256; gzip_proxied any; # Compresse aussi les réponses passant par # Traefik (sinon Nginx s'en abstient dès qu'il # détecte un proxy en amont). gzip_types text/plain text/css text/xml text/javascript application/javascript application/json application/manifest+json application/xml image/svg+xml; # ========================================================================= # CACHE # HTML revalidé à chaque visite, assets mis en cache longuement. # Sans cette distinction, un visiteur garderait l'ancienne page après # chaque redéploiement. # ========================================================================= location ~* \.(css|js)$ { expires 7d; add_header Cache-Control "public, max-age=604800"; access_log off; } location ~* \.(png|jpe?g|gif|svg|webp|avif|ico|woff2?)$ { expires 90d; add_header Cache-Control "public, max-age=7776000"; access_log off; } location = /index.html { add_header Cache-Control "public, max-age=0, must-revalidate"; } location ~* \.(txt|xml|webmanifest)$ { expires 1d; add_header Cache-Control "public, max-age=86400"; } # ========================================================================= # TYPES MIME PARTICULIERS # ========================================================================= location = /site.webmanifest { default_type application/manifest+json; add_header Cache-Control "public, max-age=86400"; } location = /llms.txt { default_type text/plain; charset utf-8; } location = /.well-known/security.txt { default_type text/plain; charset utf-8; } # ========================================================================= # SONDE DE SANTÉ # Coolify et Docker interrogent ce point pour savoir si le conteneur est # prêt. Réponse minimale, sans journalisation, pour ne pas polluer les logs. # ========================================================================= location = /healthz { access_log off; add_header Content-Type text/plain; return 200 "ok\n"; } # ========================================================================= # ROUTAGE # ========================================================================= location / { try_files $uri $uri/ /index.html; } # Bloque les fichiers cachés, sauf .well-known (RFC 8615) location ~ /\.(?!well-known) { deny all; return 404; } error_page 404 /index.html; }