Erreur 503 sur Nginx

Le guide complet pour les administrateurs système

Configuration, upstream, load balancing : maîtrisez votre reverse proxy.

Nginx et les erreurs 503

Nginx est le serveur web le plus utilisé au monde. En tant que reverse proxy, il fait le lien entre les visiteurs et vos applications (PHP-FPM, Node.js, Python…).

Quand Nginx renvoie une 503, c’est généralement pour l’une de ces raisons :

  • Le backend est down (PHP-FPM crashé, service arrêté)
  • Le backend est surchargé (tous les workers occupés)
  • Une limite est atteinte (connexions, fichiers ouverts)
  • Le load balancer n’a plus de serveur disponible
  • Une configuration retourne volontairement 503 (maintenance)

Ce guide couvre chaque scénario avec les solutions appropriées.

Besoin d’aide pour votre infrastructure ? Foxop propose des audits et de l’infogérance serveur haute disponibilité.

Comprendre l’architecture Nginx

Avant de diagnostiquer, comprenons comment Nginx gère les requêtes :

flowchart LR
    A[Client] --> B[Nginx
Port 80/443] B --> C{Type de requête ?} C -->|Fichier statique| D[Servir directement
depuis disque] C -->|PHP| E[upstream php-fpm
Socket/Port 9000] C -->|Node.js| F[upstream node
Port 3000] C -->|Autre backend| G[upstream custom
Port XXXX] E --> H{Backend répond ?} H -->|Oui| I[200 OK] H -->|Timeout| J[504 Gateway Timeout] H -->|Refusé| K[502 Bad Gateway] H -->|Surchargé| L[503 Service Unavailable]

Les codes d’erreur Nginx

CodeNomSignification Nginx
502Bad GatewayNginx n’arrive pas à contacter le backend
503Service UnavailableBackend surchargé ou maintenance active
504Gateway TimeoutBackend trop lent à répondre
499Client Closed RequestLe client a abandonné avant la réponse

La frontière entre 502 et 503 est parfois floue. Une 502 indique généralement une impossibilité de connexion, une 503 une surcharge ou une indisponibilité temporaire déclarée.

Diagnostic : Identifier la cause

Étape 1 : Vérifier les logs Nginx

# Logs d'erreur (le plus important)
$ tail -100 /var/log/nginx/error.log

# Filtrer les erreurs 503
$ grep "503" /var/log/nginx/access.log | tail -50

# Erreurs en temps réel
$ tail -f /var/log/nginx/error.log

Messages d’erreur courants et leur signification

Erreurs Nginx typiques

# Backend down
connect() failed (111: Connection refused) while connecting to upstream
→ PHP-FPM ou autre backend n'écoute pas sur le socket/port configuré

# Backend surchargé
upstream timed out (110: Connection timed out) while connecting to upstream
→ Le backend met trop de temps à accepter la connexion

# Réponse trop lente
upstream timed out (110: Connection timed out) while reading response header
→ Le backend a accepté la connexion mais ne répond pas assez vite

# Tous les backends down (load balancing)
no live upstreams while connecting to upstream
→ Aucun serveur du pool n'est disponible

# Limite de fichiers
socket() failed (24: Too many open files) while connecting to upstream
→ Limite système atteinte

# Socket introuvable
connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory)
→ Le socket PHP-FPM n'existe pas (service arrêté)

Étape 2 : Vérifier l’état des backends

# PHP-FPM
$ systemctl status php8.2-fpm
$ ps aux | grep php-fpm | head -5

# Socket existe ?
$ ls -la /run/php/php8.2-fpm.sock

# Port écoute ? (si TCP au lieu de socket)
$ ss -tlnp | grep 9000

# Node.js / autre
$ systemctl status mon-app-node
$ ss -tlnp | grep 3000

Étape 3 : Vérifier les ressources système

# Charge CPU
$ uptime

# Mémoire
$ free -h

# Connexions actives
$ ss -s
Total: 1523
TCP:   892 (estab 456, closed 234, orphaned 12, timewait 190)

# Fichiers ouverts par Nginx
$ lsof -p $(cat /run/nginx.pid) | wc -l

Problème 1 : Backend PHP-FPM down

C’est la cause n°1 des 503 sur Nginx avec PHP.

Diagnostic

# Vérifier PHP-FPM
$ systemctl status php8.2-fpm

● php8.2-fpm.service - The PHP 8.2 FastCGI Process Manager
     Active: inactive (dead) ❌

# Ou
     Active: failed (Result: exit-code) ❌

Solutions

Solution 1 : Redémarrer PHP-FPM

$ systemctl restart php8.2-fpm

# Vérifier
$ systemctl status php8.2-fpm
     Active: active (running) ✅

# Tester
$ curl -I https://monsite.com
HTTP/1.1 200 OK ✅

Solution 2 : Le socket n’existe pas

# Vérifier
$ ls -la /run/php/php8.2-fpm.sock
ls: cannot access '/run/php/php8.2-fpm.sock': No such file or directory

# Le dossier /run/php existe-t-il ?
$ ls -la /run/php/
# Si non :
$ mkdir -p /run/php
$ chown www-data:www-data /run/php

# Redémarrer
$ systemctl restart php8.2-fpm

Solution 3 : Permissions du socket

# Vérifier les permissions
$ ls -la /run/php/php8.2-fpm.sock
srw-rw---- 1 www-data www-data 0 Dec 26 14:30 /run/php/php8.2-fpm.sock

# Nginx doit pouvoir y accéder
# Vérifier l'utilisateur Nginx
$ ps aux | grep nginx
www-data  1234  ... nginx: worker process

# Si Nginx tourne en www-data et le socket appartient à www-data, c'est OK
# Sinon, ajuster dans /etc/php/8.2/fpm/pool.d/www.conf :
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

Solution 4 : PHP-FPM refuse de démarrer

# Voir pourquoi
$ journalctl -u php8.2-fpm -n 50

# Erreurs courantes :
# - "unable to bind listening socket" → Socket verrouillé
$ rm -f /run/php/php8.2-fpm.sock
$ systemctl start php8.2-fpm

# - "pool www: cannot get uid" → Utilisateur inexistant
# Vérifier user/group dans www.conf

# - Configuration invalide
$ php-fpm8.2 -t
[26-Dec-2024 14:30:00] NOTICE: configuration file /etc/php/8.2/fpm/php-fpm.conf test is successful

Problème 2 : Upstream timeout

Le backend répond mais trop lentement.

Diagnostic

$ grep "timed out" /var/log/nginx/error.log | tail -20

upstream timed out (110: Connection timed out) while reading response header from upstream
# → Le backend est lent à générer la réponse

Solutions

Solution 1 : Augmenter les timeouts Nginx

/etc/nginx/nginx.conf ou site.conf

http {
    # Timeouts globaux
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;
    
    # Pour FastCGI (PHP-FPM)
    fastcgi_connect_timeout 60s;
    fastcgi_send_timeout 300s;
    fastcgi_read_timeout 300s;
}

Solution 2 : Configuration par location

/etc/nginx/sites-available/monsite.conf

server {
    # Timeouts standard
    location ~ \\.php$ {
        fastcgi_read_timeout 60s;
        # ... reste de la config
    }
    
    # Timeouts plus longs pour les opérations lourdes
    location ~ ^/(import|export|backup) {
        fastcgi_read_timeout 600s;  # 10 minutes
        fastcgi_send_timeout 600s;
        # ... reste de la config
    }
    
    # Webhook / callbacks externes
    location /webhook {
        proxy_read_timeout 120s;
        proxy_pass http://backend;
    }
}

Solution 3 : Le vrai problème est côté backend

Augmenter les timeouts est un palliatif. Le vrai fix est d’optimiser le backend :

# Identifier les scripts lents (PHP-FPM slow log)
$ tail -50 /var/log/php-fpm/slow.log

# Activer le slow log si pas fait
# /etc/php/8.2/fpm/pool.d/www.conf
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slow.log

Problème 3 : Trop de connexions / Workers épuisés

Diagnostic Nginx

# Connexions actives
$ curl http://localhost/nginx_status
Active connections: 2456
server accepts handled requests
 1234567 1234567 9876543
Reading: 12 Writing: 234 Waiting: 2210

# Si "Active connections" très élevé et "Waiting" très élevé → Workers saturés

Pour activer /nginx_status, ajoutez dans votre config :

location /nginx_status {
    stub_status on;
    allow 127.0.0.1;
    deny all;
}

Diagnostic PHP-FPM

# Status PHP-FPM
$ curl http://localhost/fpm-status
pool:                 www
process manager:      dynamic
start time:           26/Dec/2024:10:00:00
start since:          16200
accepted conn:        123456
listen queue:         45        # ← File d'attente (devrait être 0)
max listen queue:     128
listen queue len:     511
idle processes:       0         # ← 0 idle = tous occupés !
active processes:     50        # ← = max_children
max active processes: 50
max children reached: 234       # ← Limite atteinte 234 fois !

Solutions

Solution 1 : Augmenter les workers Nginx

/etc/nginx/nginx.conf

# Nombre de workers (auto = 1 par CPU)
worker_processes auto;

# Connexions par worker
events {
    worker_connections 4096;  # Défaut: 768
    use epoll;
    multi_accept on;
}

Solution 2 : Augmenter les workers PHP-FPM

/etc/php/8.2/fpm/pool.d/www.conf

; Calculer selon RAM disponible
; max_children = RAM disponible (Mo) / Mémoire par process (~100-150 Mo)
; Exemple : 8 Go RAM = 8000 / 150 = ~50 workers

pm = dynamic
pm.max_children = 50          ; Maximum de workers
pm.start_servers = 10         ; Au démarrage
pm.min_spare_servers = 5      ; Minimum en attente
pm.max_spare_servers = 20     ; Maximum en attente
pm.max_requests = 500         ; Recycle après X requêtes (évite memory leaks)
$ systemctl restart php8.2-fpm

Solution 3 : Limiter les connexions par IP (protection)

/etc/nginx/nginx.conf

http {
    # Zones de limitation
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    limit_req_zone $binary_remote_addr zone=req:10m rate=10r/s;
}

Problème 4 : Load Balancing – Tous les backends down

Si vous utilisez Nginx comme load balancer, une 503 peut signifier qu’aucun backend n’est disponible.

Configuration load balancer type

/etc/nginx/conf.d/upstream.conf

upstream backend_pool {
    # Méthode de répartition
    least_conn;  # ou round-robin (défaut), ip_hash
    
    # Serveurs backend
    server 192.168.1.10:80 weight=5;
    server 192.168.1.11:80 weight=3;
    server 192.168.1.12:80 backup;  # Utilisé si les autres sont down
    
    # Health checks (Nginx Plus) ou passive checks
    # max_fails=3 : 3 échecs avant de marquer down
    # fail_timeout=30s : Durée pendant laquelle le serveur est considéré down
    server 192.168.1.10:80 max_fails=3 fail_timeout=30s;
    
    # Keepalive connections
    keepalive 32;
}

Diagnostic

$ grep "no live upstreams" /var/log/nginx/error.log
no live upstreams while connecting to upstream

# Vérifier chaque backend manuellement
$ curl -I http://192.168.1.10
$ curl -I http://192.168.1.11
$ curl -I http://192.168.1.12

Solutions

Solution 1 : Ajouter un serveur de backup

upstream.conf

upstream backend_pool {
    server 192.168.1.10:80;
    server 192.168.1.11:80;
    
    # Serveur de secours (page maintenance statique)
    server 192.168.1.99:80 backup;
}

Solution 2 : Page d’erreur personnalisée

site.conf

server {
    # Page 503 personnalisée
    error_page 503 /maintenance.html;
    
    location = /maintenance.html {
        root /var/www/errors;
        internal;
        
        # Headers SEO
        add_header Retry-After 3600 always;
        add_header Cache-Control "no-store, no-cache" always;
    }
    
    location / {
        proxy_pass http://backend_pool;
        
        # Intercepter les 503 du backend
        proxy_intercept_errors on;
    }
}

Solution 3 : Health checks actifs (Nginx Plus ou module tiers)

Pour Nginx Plus

upstream backend_pool {
    zone backend 64k;
    
    server 192.168.1.10:80;
    server 192.168.1.11:80;
    
    # Health check actif
    health_check interval=5s fails=3 passes=2;
}

Pour Nginx open source, utilisez un script externe :

/usr/local/bin/check-backends.sh

#!/bin/bash
# Health check simple pour backends

BACKENDS="192.168.1.10 192.168.1.11"
ALERT_EMAIL="[email protected]"

for backend in $BACKENDS; do
    if ! curl -sf --max-time 5 "http://$backend/health" > /dev/null; then
        echo "$(date): Backend $backend DOWN" >> /var/log/backend-health.log
        echo "Backend $backend is down" | mail -s "ALERT: Backend down" $ALERT_EMAIL
    fi
done

Problème 5 : Retourner 503 volontairement (maintenance)

Méthode 1 : Fichier flag

/etc/nginx/sites-available/monsite.conf

server {
    listen 80;
    server_name monsite.com;
    root /var/www/monsite;
    
    # Vérifier si fichier maintenance existe
    set $maintenance 0;
    if (-f /var/www/monsite/.maintenance) {
        set $maintenance 1;
    }
    
    # Exclure certaines IPs
    if ($remote_addr = "123.456.789.012") {
        set $maintenance 0;
    }
    
    # Retourner 503 si maintenance
    if ($maintenance = 1) {
        return 503;
    }
    
    # Page de maintenance personnalisée
    error_page 503 @maintenance;
    
    location @maintenance {
        add_header Retry-After 3600 always;
        add_header Cache-Control "no-store, no-cache" always;
        rewrite ^(.*)$ /maintenance.html break;
    }
    
    # Configuration normale...
}

Activation / Désactivation :

# Activer
$ touch /var/www/monsite/.maintenance

# Désactiver
$ rm /var/www/monsite/.maintenance

# Pas besoin de recharger Nginx !

Méthode 2 : Variable dans la config

/etc/nginx/sites-available/monsite.conf

server {
    # Basculer manuellement : 0 = off, 1 = on
    set $maintenance_mode 0;
    
    # IPs autorisées même en maintenance
    geo $maintenance_bypass {
        default 0;
        123.456.789.012 1;  # Admin 1
        98.76.54.321 1;     # Admin 2
    }
    
    # Logique de maintenance
    set $show_maintenance "${maintenance_mode}${maintenance_bypass}";
    
    if ($show_maintenance = "10") {
        return 503;
    }
    
    error_page 503 @maintenance;
    
    location @maintenance {
        root /var/www/errors;
        add_header Retry-After 7200 always;
        try_files /maintenance.html =503;
    }
}

Activation :

# Modifier set $maintenance_mode 1;
$ sudo nano /etc/nginx/sites-available/monsite.conf

# Recharger
$ sudo nginx -t && sudo nginx -s reload

Méthode 3 : Maintenance par période (cron)

/etc/nginx/conf.d/maintenance-schedule.conf

# Carte horaire de maintenance
# Format : HHMM
map $time_iso8601 $maintenance_window {
    default 0;
    "~^.{11}0[234]:"  1;  # Entre 02:00 et 04:59
}

Configuration Nginx optimisée complète

Voici une configuration de référence pour un site en production :

/etc/nginx/nginx.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
worker_rlimit_nofile 65535;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

http {
    # === BASIC ===
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    types_hash_max_size 2048;
    server_tokens off;  # Masquer version Nginx
    
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    # === LOGGING ===
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" $request_time';
    
    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log warn;
    
    # === TIMEOUTS ===
    keepalive_timeout 65;
    client_body_timeout 12;
    client_header_timeout 12;
    send_timeout 10;
    
    # === BUFFERS ===
    client_body_buffer_size 10K;
    client_header_buffer_size 1k;
    client_max_body_size 64M;
    large_client_header_buffers 4 8k;
    
    # === GZIP ===
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 5;
    gzip_min_length 256;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml
        application/xml+rss
        image/svg+xml;
    
    # === RATE LIMITING ===
    limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    
    # === FASTCGI (PHP) ===
    fastcgi_connect_timeout 60s;
    fastcgi_send_timeout 180s;
    fastcgi_read_timeout 180s;
    fastcgi_buffer_size 128k;
    fastcgi_buffers 256 16k;
    fastcgi_busy_buffers_size 256k;
    fastcgi_temp_file_write_size 256k;
    
    # === PROXY (si reverse proxy) ===
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;
    proxy_buffer_size 128k;
    proxy_buffers 4 256k;
    proxy_busy_buffers_size 256k;
    
    # === INCLUDES ===
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

/etc/nginx/sites-available/monsite.conf

server {
    listen 80;
    server_name monsite.com www.monsite.com;
    return 301 https://monsite.com$request_uri;
}

server {
    listen 443 ssl http2;
    server_name monsite.com;
    root /var/www/monsite;
    index index.php index.html;
    
    # === SSL ===
    ssl_certificate /etc/letsencrypt/live/monsite.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/monsite.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    
    # === MAINTENANCE MODE ===
    set $maintenance 0;
    if (-f /var/www/monsite/.maintenance) {
        set $maintenance 1;
    }
    # Whitelist IPs
    if ($remote_addr = "VOTRE_IP") {
        set $maintenance 0;
    }
    if ($maintenance = 1) {
        return 503;
    }
    
    error_page 503 @maintenance;
    location @maintenance {
        add_header Retry-After 3600 always;
        add_header Cache-Control "no-store" always;
        root /var/www/errors;
        try_files /maintenance.html =503;
    }
    
    # === SECURITY ===
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    
    # === RATE LIMITING ===
    limit_conn addr 20;
    limit_req zone=general burst=20 nodelay;
    
    # === LOCATIONS ===
    
    # Fichiers statiques
    location ~* \\.(jpg|jpeg|png|gif|ico|css|js|woff2?|svg|webp)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
        access_log off;
    }
    
    # PHP
    location ~ \\.php$ {
        try_files $uri =404;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
    
    # Login / Admin - Rate limiting strict
    location ~ ^/(wp-login\\.php|admin|administrator) {
        limit_req zone=login burst=5 nodelay;
        try_files $uri =404;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
    
    # Bloquer xmlrpc (WordPress)
    location = /xmlrpc.php {
        deny all;
        return 444;
    }
    
    # Status pages (accès local uniquement)
    location /nginx_status {
        stub_status on;
        allow 127.0.0.1;
        deny all;
    }
    
    location /fpm-status {
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        allow 127.0.0.1;
        deny all;
    }
    
    # Fallback
    location / {
        try_files $uri $uri/ /index.php?$args;
    }
}

Commandes Nginx essentielles

# === GESTION DU SERVICE ===

# Démarrer / Arrêter / Redémarrer
$ systemctl start nginx
$ systemctl stop nginx
$ systemctl restart nginx

# Recharger la config (sans interruption)
$ systemctl reload nginx
# ou
$ nginx -s reload

# === TEST ET DIAGNOSTIC ===

# Tester la configuration
$ nginx -t
nginx: configuration file /etc/nginx/nginx.conf test is successful

# Voir la config effective
$ nginx -T

# Version et modules compilés
$ nginx -V

# === LOGS ===

# Erreurs en temps réel
$ tail -f /var/log/nginx/error.log

# Accès en temps réel
$ tail -f /var/log/nginx/access.log

# Filtrer les 5xx
$ grep " 50[0-9] " /var/log/nginx/access.log | tail -50

# === MONITORING ===

# Connexions actives (si stub_status activé)
$ curl -s http://localhost/nginx_status

# Processus Nginx
$ ps aux | grep nginx

# Ports écoutés
$ ss -tlnp | grep nginx

# === CACHE ===

# Vider le cache proxy (si configuré)
$ rm -rf /var/cache/nginx/*

# === DEBUG ===

# Activer le debug logging temporairement
# Dans nginx.conf : error_log /var/log/nginx/error.log debug;
# Puis : nginx -s reload
# ATTENTION : génère BEAUCOUP de logs

Monitoring et alertes

Script de surveillance

/usr/local/bin/check-nginx-health.sh

#!/bin/bash
# Surveillance santé Nginx + PHP-FPM

ALERT_EMAIL="[email protected]"
SITE_URL="https://monsite.com"

# Vérifier que Nginx tourne
if ! systemctl is-active --quiet nginx; then
    echo "CRITICAL: Nginx is down" | mail -s "[ALERT] Nginx down" $ALERT_EMAIL
    systemctl restart nginx
    exit 1
fi

# Vérifier que PHP-FPM tourne
if ! systemctl is-active --quiet php8.2-fpm; then
    echo "CRITICAL: PHP-FPM is down" | mail -s "[ALERT] PHP-FPM down" $ALERT_EMAIL
    systemctl restart php8.2-fpm
    exit 1
fi

# Vérifier que le site répond
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$SITE_URL")

if [ "$HTTP_CODE" != "200" ]; then
    echo "WARNING: Site returned HTTP $HTTP_CODE" | mail -s "[ALERT] Site HTTP $HTTP_CODE" $ALERT_EMAIL
    exit 1
fi

# Vérifier les workers PHP disponibles
FPM_STATUS=$(curl -s "http://localhost/fpm-status" 2>/dev/null)
IDLE=$(echo "$FPM_STATUS" | grep "idle processes" | awk '{print $3}')

if [ -n "$IDLE" ] && [ "$IDLE" -lt 2 ]; then
    echo "WARNING: Only $IDLE idle PHP workers" | mail -s "[WARN] PHP workers low" $ALERT_EMAIL
fi

exit 0
# Cron toutes les minutes
$ crontab -e
* * * * * /usr/local/bin/check-nginx-health.sh >> /var/log/nginx-health.log 2>&1

Intégration avec des outils de monitoring

# Prometheus + nginx-exporter
$ apt install prometheus-nginx-exporter

# Datadog
$ DD_API_KEY=xxx DD_SITE="datadoghq.eu" bash -c "$(curl -L https://install.datadoghq.com/scripts/install_script.sh)"

# Netdata (simple et gratuit)
$ bash <(curl -Ss https://my-netdata.io/kickstart.sh)

Infrastructure haute disponibilité

Load balancing, failover automatique, monitoring 24/7.

Questions fréquentes

Quelle est la différence entre nginx -s reload et systemctl restart nginx ?

nginx -s reload recharge la configuration sans interrompre les connexions en cours (graceful). systemctl restart nginx arrête et redémarre complètement le processus, coupant toutes les connexions. Utilisez toujours reload en production sauf si nécessaire de redémarrer complètement.

Comment debugger une configuration Nginx complexe ?

1) Utilisez nginx -t pour valider la syntaxe. 2) Activez temporairement error_log ... debug; pour des logs détaillés. 3) Utilisez nginx -T pour voir la configuration effective (après includes). 4) Testez avec curl -v pour voir les headers de réponse.

TCP socket ou Unix socket pour PHP-FPM ?

Unix socket (/run/php/php-fpm.sock) est plus rapide car il évite la stack TCP/IP. Utilisez-le si Nginx et PHP-FPM sont sur la même machine. TCP (127.0.0.1:9000) est nécessaire si PHP-FPM est sur un autre serveur ou dans un container séparé.

Comment gérer plusieurs sites avec différentes versions de PHP ?

Créez plusieurs pools PHP-FPM, chacun avec son socket. Dans chaque vhost Nginx, pointez vers le bon socket : fastcgi_pass unix:/run/php/php7.4-fpm-site1.sock; pour un site, fastcgi_pass unix:/run/php/php8.2-fpm-site2.sock; pour un autre.