Erreur 503 sur Apache

Le guide complet pour les administrateurs Apache

MPM, mod_proxy, .htaccess : maîtrisez votre serveur web.

Apache et les erreurs 503

Apache HTTP Server reste le serveur web le plus déployé, notamment sur les hébergements mutualisés, les environnements cPanel/Plesk, et de nombreuses infrastructures d’entreprise.

Les erreurs 503 sur Apache surviennent généralement dans ces contextes :

  • Le backend PHP-FPM ou mod_php est down/surchargé
  • Les workers MPM sont tous occupés
  • Un reverse proxy (mod_proxy) ne peut joindre le backend
  • Une règle .htaccess retourne explicitement 503
  • Les limites système sont atteintes (connexions, fichiers)

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

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

Comprendre l’architecture Apache

Les différents modes de traitement (MPM)

Apache peut fonctionner avec différents MPM (Multi-Processing Modules) :

MPMDescriptionUsage recommandé
prefork1 processus par requête, pas de threadsmod_php (legacy), compatibilité maximale
workerProcessus + threadsPerformance, mémoire optimisée
eventComme worker + gestion async des keepalivesRecommandé avec PHP-FPM
# Voir le MPM actif
$ apachectl -V | grep MPM
Server MPM:     event

# Ou
$ apache2ctl -M | grep mpm
 mpm_event_module (shared)

Architecture avec PHP-FPM (recommandé)

flowchart LR
    A[Client] --> B[Apache
Port 80/443] B --> C{Type de requête ?} C -->|Fichier statique| D[Servir directement] C -->|PHP| E[mod_proxy_fcgi
→ PHP-FPM] E --> F[Socket/Port 9000] F --> G{PHP-FPM répond ?} G -->|Oui| H[200 OK] G -->|Down| I[503 Service Unavailable] G -->|Timeout| J[504 Gateway Timeout]

Architecture avec mod_php (legacy)

flowchart LR
    A[Client] --> B[Apache + mod_php
Port 80/443] B --> C{Type de requête ?} C -->|Fichier statique| D[Servir directement] C -->|PHP| E[mod_php
Dans le processus Apache] E --> F{Ressources OK ?} F -->|Oui| G[200 OK] F -->|Mémoire épuisée| H[503] F -->|Workers épuisés| I[503]

mod_php est déprécié pour les nouvelles installations. Préférez PHP-FPM avec mod_proxy_fcgi pour de meilleures performances et une meilleure isolation.

Diagnostic : Identifier la cause

Étape 1 : Vérifier les logs Apache

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

# Sur CentOS/RHEL
$ tail -100 /var/log/httpd/error_log

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

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

Messages d’erreur courants

Erreurs Apache typiques

# PHP-FPM down
[proxy_fcgi:error] [pid 1234] (111)Connection refused: AH01079: 
failed to make connection to backend: /run/php/php8.2-fpm.sock
→ PHP-FPM n'écoute pas sur le socket

# Backend timeout
[proxy_fcgi:error] [pid 1234] AH01075: Error dispatching request to : 
(polling) backend connection timed out
→ PHP-FPM ne répond pas assez vite

# Tous les workers occupés
[mpm_event:error] [pid 1234] AH00485: scoreboard is full, not at MaxRequestWorkers
→ Plus de workers disponibles

# Limite de fichiers atteinte
[core:error] [pid 1234] (24)Too many open files: AH00024: 
Couldn't create accept lock
→ Limite système ulimit atteinte

# mod_php memory exhausted
[core:notice] [pid 1234] PHP Fatal error: Allowed memory size exhausted
→ Script PHP dépasse la limite mémoire

Étape 2 : Vérifier l’état d’Apache

# État du service
$ systemctl status apache2
# ou sur CentOS/RHEL
$ systemctl status httpd

# Processus Apache
$ ps aux | grep apache2 | head -10

# Connexions actives
$ ss -tlnp | grep apache2

# Workers actifs (si mod_status activé)
$ curl -s http://localhost/server-status?auto | grep -E "^(Busy|Idle)Workers"
BusyWorkers: 45
IdleWorkers: 5

Étape 3 : Vérifier les backends

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

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

# Test de connexion au socket
$ php -r "var_dump(stream_socket_client('unix:///run/php/php8.2-fpm.sock'));"

Problème 1 : Backend PHP-FPM down

La cause la plus fréquente avec les configurations modernes Apache + PHP-FPM.

Diagnostic

$ grep "proxy_fcgi" /var/log/apache2/error.log | tail -20

[proxy_fcgi:error] AH01079: failed to make connection to backend: /run/php/php8.2-fpm.sock

# Vérifier PHP-FPM
$ systemctl status php8.2-fpm
     Active: inactive (dead) ❌

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 : Vérifier la configuration proxy

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

<VirtualHost *:443>
    ServerName monsite.com
    DocumentRoot /var/www/monsite
    
    # Configuration PHP-FPM via socket Unix
    <FilesMatch \\.php$>
        SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost"
    </FilesMatch>
    
    # Ou via TCP
    # <FilesMatch \\.php$>
    #     SetHandler "proxy:fcgi://127.0.0.1:9000"
    # </FilesMatch>
</VirtualHost>

Solution 3 : Vérifier que les modules sont chargés

# Modules nécessaires pour PHP-FPM
$ apache2ctl -M | grep -E "proxy|fcgi"
 proxy_module (shared)
 proxy_fcgi_module (shared)

# Si manquants
$ a2enmod proxy proxy_fcgi
$ systemctl restart apache2

Solution 4 : 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

# Apache doit pouvoir y accéder
$ ps aux | grep apache2 | head -1
www-data  1234  ... /usr/sbin/apache2

# Si Apache ne tourne pas en www-data, ajuster dans PHP-FPM :
# /etc/php/8.2/fpm/pool.d/www.conf
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

Problème 2 : Workers MPM épuisés

Tous les workers Apache sont occupés, les nouvelles requêtes sont refusées.

Diagnostic

# Voir les workers actifs
$ curl -s http://localhost/server-status?auto 2>/dev/null | grep Workers
BusyWorkers: 150
IdleWorkers: 0  # ← Aucun worker libre !

# Ou via les processus
$ ps aux | grep apache2 | wc -l
152

# Logs
$ grep "scoreboard is full" /var/log/apache2/error.log
[mpm_event:error] AH00485: scoreboard is full, not at MaxRequestWorkers

Solutions

Solution 1 : Augmenter les workers MPM Event

/etc/apache2/mods-available/mpm_event.conf

<IfModule mpm_event_module>
    # Processus serveur
    StartServers             4
    MinSpareThreads          75
    MaxSpareThreads          250
    
    # Threads par processus
    ThreadsPerChild          25
    
    # Maximum total de connexions simultanées
    # MaxRequestWorkers = ServerLimit * ThreadsPerChild
    MaxRequestWorkers        400
    ServerLimit              16
    
    # Connexions en file d'attente
    ListenBacklog            511
    
    # Recyclage des processus (évite memory leaks)
    MaxConnectionsPerChild   10000
</IfModule>
$ systemctl restart apache2

Solution 2 : Augmenter les workers MPM Prefork (si mod_php)

/etc/apache2/mods-available/mpm_prefork.conf

<IfModule mpm_prefork_module>
    StartServers             5
    MinSpareServers          5
    MaxSpareServers          10
    
    # Maximum de processus simultanés
    # Attention : chaque processus consomme ~50-100 Mo avec mod_php
    MaxRequestWorkers        150
    ServerLimit              150
    
    MaxConnectionsPerChild   3000
</IfModule>

Avec mod_php (prefork), chaque worker consomme beaucoup de RAM. 150 workers × 100 Mo = 15 Go de RAM. Calculez selon votre serveur.

Solution 3 : Passer à MPM Event + PHP-FPM

Si vous êtes encore en prefork + mod_php, la migration vers event + PHP-FPM améliore drastiquement les performances :

# Désactiver mod_php et prefork
$ a2dismod php8.2
$ a2dismod mpm_prefork

# Activer event et proxy_fcgi
$ a2enmod mpm_event
$ a2enmod proxy_fcgi

# Configurer PHP-FPM
$ a2enconf php8.2-fpm

$ systemctl restart apache2

Problème 3 : Timeout backend

Le backend répond mais trop lentement.

Diagnostic

$ grep -i "timeout" /var/log/apache2/error.log | tail -20

[proxy_fcgi:error] AH01075: Error dispatching request: (polling) backend connection timed out

Solutions

Solution 1 : Augmenter les timeouts proxy

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

<VirtualHost *:443>
    ServerName monsite.com
    
    # Timeouts proxy globaux
    ProxyTimeout 300
    
    # Ou par location
    <LocationMatch "^/(import|export|backup)">
        ProxyTimeout 600
    </LocationMatch>
    
    # Configuration PHP-FPM avec timeout
    <FilesMatch \\.php$>
        SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost"
        
        # Timeout spécifique pour PHP
        ProxySet timeout=300
    </FilesMatch>
</VirtualHost>

Solution 2 : Timeouts globaux Apache

/etc/apache2/apache2.conf

# Timeout général (défaut: 60)
Timeout 300

# Timeout keepalive
KeepAliveTimeout 5

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

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

$ systemctl restart php8.2-fpm

# Voir les scripts lents
$ tail -50 /var/log/php-fpm/slow.log

Problème 4 : Configuration .htaccess

Le fichier .htaccess peut retourner explicitement une 503 ou créer des boucles.

Diagnostic

# Vérifier les .htaccess récemment modifiés
$ find /var/www -name ".htaccess" -mtime -1

# Chercher des règles 503
$ grep -r "503\\|maintenance" /var/www/*/.htaccess

Configuration maintenance via .htaccess

.htaccess

# ============================================
# MODE MAINTENANCE
# Décommenter pour activer
# ============================================

RewriteEngine On

# 1. Exclure votre IP
RewriteCond %{REMOTE_ADDR} !^123\\.456\\.789\\.012$
# Ajouter d'autres IPs si besoin
# RewriteCond %{REMOTE_ADDR} !^111\\.222\\.333\\.444$

# 2. Exclure les fichiers statiques (optionnel)
RewriteCond %{REQUEST_URI} !\\.(css|js|png|jpg|gif|svg|ico|woff2?)$ [NC]

# 3. Exclure la page maintenance elle-même
RewriteCond %{REQUEST_URI} !^/maintenance\\.html$

# 4. Rediriger vers 503
RewriteRule ^(.*)$ /maintenance.html [R=503,L]

# 5. Header Retry-After
<IfModule mod_headers.c>
    Header always set Retry-After "3600"
    Header always set Cache-Control "no-store, no-cache, must-revalidate"
</IfModule>

# 6. ErrorDocument personnalisé
ErrorDocument 503 /maintenance.html

Attention : Le RewriteRule avec [R=503,L] fonctionne mais certaines versions d’Apache ne renvoient pas correctement le code 503. Testez avec curl -I.

Méthode alternative plus fiable

.htaccess

# Méthode avec ErrorDocument (plus fiable)

<IfModule mod_rewrite.c>
    RewriteEngine On
    
    # Créer une condition de maintenance
    RewriteCond %{DOCUMENT_ROOT}/maintenance.enable -f
    RewriteCond %{REMOTE_ADDR} !^123\\.456\\.789\\.012$
    RewriteCond %{REQUEST_URI} !^/maintenance\\.html$
    RewriteCond %{REQUEST_URI} !\\.(css|js|png|jpg|gif)$ [NC]
    RewriteRule ^ - [E=MAINTENANCE:1]
</IfModule>

<IfModule mod_headers.c>
    <If "%{ENV:MAINTENANCE} == '1'">
        Header always set Retry-After "3600"
    </If>
</IfModule>

# Si variable MAINTENANCE définie, retourner 503
<If "%{ENV:MAINTENANCE} == '1'">
    ErrorDocument 503 /maintenance.html
    # Forcer le 503
    RedirectMatch 503 ^/(?!maintenance\\.html)
</If>

Activation :

# Créer le fichier flag pour activer
$ touch /var/www/monsite/maintenance.enable

# Supprimer pour désactiver
$ rm /var/www/monsite/maintenance.enable

Problème 5 : Limites système atteintes

Trop de fichiers ouverts

$ grep "Too many open files" /var/log/apache2/error.log
[core:error] (24)Too many open files: AH00024: Couldn't create accept lock

Solution :

# Voir la limite actuelle
$ ulimit -n
1024  # Trop bas !

# Augmenter pour Apache
$ cat >> /etc/security/limits.conf << EOF
www-data soft nofile 65535
www-data hard nofile 65535
apache soft nofile 65535
apache hard nofile 65535
EOF

# Pour systemd
$ mkdir -p /etc/systemd/system/apache2.service.d/
$ cat > /etc/systemd/system/apache2.service.d/limits.conf << EOF
[Service]
LimitNOFILE=65535
EOF

$ systemctl daemon-reload
$ systemctl restart apache2

# Vérifier
$ cat /proc/$(cat /var/run/apache2/apache2.pid)/limits | grep "open files"
Max open files            65535                65535                files

Mémoire insuffisante

$ dmesg | grep -i "out of memory"
$ grep -i "killed process" /var/log/syslog | grep apache

Solution :

/etc/apache2/mods-available/mpm_event.conf

# Réduire les workers pour économiser la RAM
<IfModule mpm_event_module>
    MaxRequestWorkers        150  # Réduire si OOM
    MaxConnectionsPerChild   5000 # Recycler plus souvent
</IfModule>

Connexions TCP saturées

# Voir les connexions
$ ss -s
TCP:   8234 (estab 4521, closed 2100, orphaned 45, timewait 1568)

# Si beaucoup de TIME_WAIT
$ cat /proc/sys/net/ipv4/tcp_fin_timeout
60

# Réduire
$ echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout

# Permanent
$ echo "net.ipv4.tcp_fin_timeout = 30" >> /etc/sysctl.conf
$ sysctl -p

Problème 6 : Reverse Proxy (mod_proxy)

Si Apache sert de reverse proxy vers d’autres backends.

Configuration type

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

<VirtualHost *:443>
    ServerName monsite.com
    
    # Activer le proxy
    ProxyPreserveHost On
    ProxyRequests Off
    
    # Backend principal
    ProxyPass / http://127.0.0.1:3000/
    ProxyPassReverse / http://127.0.0.1:3000/
    
    # Timeouts
    ProxyTimeout 60
    
    # Health check passif
    ProxyPass / http://127.0.0.1:3000/ retry=5 timeout=60
</VirtualHost>

Diagnostic

$ grep "proxy:" /var/log/apache2/error.log | tail -20

[proxy:error] AH00959: ap_proxy_connect_backend disabling worker for (127.0.0.1)
# → Backend marqué comme down

[proxy_http:error] AH01114: HTTP: failed to make connection to backend: 127.0.0.1
# → Impossible de se connecter

Solutions

Solution 1 : Load balancing avec failover

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

<VirtualHost *:443>
    ServerName monsite.com
    
    # Pool de backends
    <Proxy "balancer://mycluster">
        BalancerMember http://192.168.1.10:3000 loadfactor=5
        BalancerMember http://192.168.1.11:3000 loadfactor=3
        BalancerMember http://192.168.1.12:3000 status=+H  # Hot standby
        
        ProxySet lbmethod=byrequests
        ProxySet failontimeout=on
        ProxySet timeout=30
    </Proxy>
    
    ProxyPass / balancer://mycluster/
    ProxyPassReverse / balancer://mycluster/
</VirtualHost>

Solution 2 : Page d’erreur personnalisée

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

<VirtualHost *:443>
    # Page 503 personnalisée
    ErrorDocument 503 /errors/maintenance.html
    
    # Alias pour servir les erreurs même si proxy down
    Alias /errors /var/www/errors
    <Directory /var/www/errors>
        Require all granted
    </Directory>
    
    # Headers SEO sur les erreurs
    <Location /errors/maintenance.html>
        Header always set Retry-After "3600"
    </Location>
    
    ProxyPass /errors !  # Ne pas proxifier les erreurs
    ProxyPass / http://backend:3000/
</VirtualHost>

Solution 3 : Réactiver un backend désactivé

# Si un backend est marqué comme "in error state"
# Il faut attendre le retry ou redémarrer Apache

$ systemctl reload apache2
# ou forcer avec
$ systemctl restart apache2

Configuration Apache optimisée complète

/etc/apache2/apache2.conf

# === IDENTIFICATION ===
ServerTokens Prod
ServerSignature Off

# === TIMEOUTS ===
Timeout 60
KeepAlive On
KeepAliveTimeout 5
MaxKeepAliveRequests 100

# === DIRECTORIES ===
<Directory />
    AllowOverride None
    Require all denied
</Directory>

<Directory /var/www/>
    Options -Indexes +FollowSymLinks
    AllowOverride All
    Require all granted
</Directory>

# === LOGGING ===
LogLevel warn
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined

# === MODULES ===
# Charger les modules nécessaires via a2enmod

/etc/apache2/mods-available/mpm_event.conf

<IfModule mpm_event_module>
    StartServers             4
    MinSpareThreads          75
    MaxSpareThreads          250
    ThreadsPerChild          25
    MaxRequestWorkers        400
    ServerLimit              16
    MaxConnectionsPerChild   10000
    ListenBacklog            511
</IfModule>

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

<VirtualHost *:80>
    ServerName monsite.com
    ServerAlias www.monsite.com
    Redirect permanent / https://monsite.com/
</VirtualHost>

<VirtualHost *:443>
    ServerName monsite.com
    DocumentRoot /var/www/monsite
    
    # === SSL ===
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/monsite.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/monsite.com/privkey.pem
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
    
    # === SECURITY HEADERS ===
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-XSS-Protection "1; mode=block"
    
    # === PHP-FPM ===
    <FilesMatch \\.php$>
        SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost"
    </FilesMatch>
    
    # === STATIC FILES ===
    <FilesMatch "\\.(jpg|jpeg|png|gif|ico|css|js|woff2?|svg|webp)$">
        ExpiresActive On
        ExpiresDefault "access plus 30 days"
        Header set Cache-Control "public, immutable"
    </FilesMatch>
    
    # === MAINTENANCE MODE ===
    # Méthode via fichier flag
    <IfModule mod_rewrite.c>
        RewriteEngine On
        
        # Si fichier .maintenance existe
        RewriteCond %{DOCUMENT_ROOT}/.maintenance -f
        # Exclure votre IP
        RewriteCond %{REMOTE_ADDR} !^VOTRE_IP$
        # Exclure la page maintenance
        RewriteCond %{REQUEST_URI} !^/maintenance\\.html$
        RewriteCond %{REQUEST_URI} !\\.(css|js|png|jpg|gif)$ [NC]
        # Retourner 503
        RewriteRule ^ - [R=503,L]
    </IfModule>
    
    ErrorDocument 503 /maintenance.html
    
    <Location /maintenance.html>
        Header always set Retry-After "3600"
        Header always set Cache-Control "no-store"
    </Location>
    
    # === RATE LIMITING ===
    <IfModule mod_ratelimit.c>
        <Location /api/>
            SetOutputFilter RATE_LIMIT
            SetEnv rate-limit 500
        </Location>
    </IfModule>
    
    # === PROTECTION ===
    # Bloquer xmlrpc.php (WordPress)
    <Files xmlrpc.php>
        Require all denied
    </Files>
    
    # Limiter accès admin
    <LocationMatch "^/(wp-admin|admin|administrator)">
        # Rate limiting via mod_evasive ou fail2ban
    </LocationMatch>
    
    # === LOGS ===
    ErrorLog ${APACHE_LOG_DIR}/monsite-error.log
    CustomLog ${APACHE_LOG_DIR}/monsite-access.log combined
</VirtualHost>

Commandes Apache essentielles

# === GESTION DU SERVICE ===

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

# Recharger la config (graceful, sans couper les connexions)
$ systemctl reload apache2
# ou
$ apachectl graceful

# === TEST ET DIAGNOSTIC ===

# Tester la configuration
$ apachectl configtest
# ou
$ apache2ctl -t
Syntax OK

# Voir la configuration effective
$ apachectl -S

# Voir les modules chargés
$ apachectl -M

# Version et compilation
$ apachectl -V

# === MODULES ===

# Activer un module
$ a2enmod rewrite
$ a2enmod headers
$ a2enmod proxy_fcgi
$ a2enmod ssl

# Désactiver un module
$ a2dismod php8.2

# === SITES ===

# Activer un site
$ a2ensite monsite.conf

# Désactiver un site
$ a2dissite 000-default.conf

# === LOGS ===

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

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

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

# === MONITORING ===

# Server status (si mod_status activé)
$ curl -s http://localhost/server-status?auto

# Processus Apache
$ ps aux | grep apache2

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

# === PERFORMANCE ===

# Benchmark simple
$ ab -n 1000 -c 50 https://monsite.com/

Activer mod_status pour le monitoring

/etc/apache2/mods-available/status.conf

<IfModule mod_status.c>
    <Location /server-status>
        SetHandler server-status
        
        # Accès local uniquement
        Require local
        # Ou depuis IP spécifique
        # Require ip 192.168.1.0/24
    </Location>
    
    # Statistiques étendues
    ExtendedStatus On
</IfModule>
$ a2enmod status
$ systemctl reload apache2

# Consulter
$ curl http://localhost/server-status?auto

Total Accesses: 123456
Total kBytes: 456789
Uptime: 86400
ReqPerSec: 1.42
BytesPerSec: 5432
BytesPerReq: 3809
BusyWorkers: 12
IdleWorkers: 38

Script de surveillance

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

#!/bin/bash
# Surveillance Apache + PHP-FPM

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

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

# Vérifier PHP-FPM
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 le site
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$SITE_URL")

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

# Vérifier les workers (si mod_status)
BUSY=$(curl -s "http://localhost/server-status?auto" 2>/dev/null | grep "BusyWorkers" | awk '{print $2}')
IDLE=$(curl -s "http://localhost/server-status?auto" 2>/dev/null | grep "IdleWorkers" | awk '{print $2}')

if [ -n "$IDLE" ] && [ "$IDLE" -lt 5 ]; then
    echo "WARNING: Only $IDLE idle workers (Busy: $BUSY)" | mail -s "[WARN] Apache workers low" $ALERT_EMAIL
fi

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

Migration de mod_php vers PHP-FPM

Si vous êtes encore sur une configuration legacy avec mod_php :

Étape 1 : Installer PHP-FPM

$ apt install php8.2-fpm
$ systemctl enable php8.2-fpm
$ systemctl start php8.2-fpm

Étape 2 : Configurer Apache

# Désactiver mod_php
$ a2dismod php8.2

# Désactiver MPM prefork
$ a2dismod mpm_prefork

# Activer MPM event et proxy_fcgi
$ a2enmod mpm_event
$ a2enmod proxy
$ a2enmod proxy_fcgi

# Activer la configuration PHP-FPM
$ a2enconf php8.2-fpm

Étape 3 : Mettre à jour les vhosts

Avant (mod_php)

<VirtualHost *:80>
    # PHP géré automatiquement par mod_php
</VirtualHost>

Après (PHP-FPM)

<VirtualHost *:80>
    <FilesMatch \\.php$>
        SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost"
    </FilesMatch>
</VirtualHost>

Étape 4 : Redémarrer

$ apache2ctl configtest
$ systemctl restart apache2

Avantages de la migration

Aspectmod_php (prefork)PHP-FPM (event)
RAM par worker~100-150 Mo~10-20 Mo
Connexions simultanéesLimitéesBeaucoup plus
IsolationAucuneProcess séparés
Redémarrage PHPRedémarre ApacheIndépendant
PerformanceCorrecteNettement meilleure

Infrastructure haute disponibilité

Audit, optimisation, migration, monitoring 24/7.

Questions fréquentes

Quelle est la différence entre apachectl graceful et systemctl restart ?

apachectl graceful (ou systemctl reload apache2) recharge la configuration sans interrompre les connexions en cours : les workers existants terminent leurs requêtes avant de mourir. systemctl restart arrête et redémarre complètement Apache, coupant toutes les connexions. Utilisez graceful en production sauf si un redémarrage complet est nécessaire.

Comment savoir si je dois utiliser prefork, worker ou event ?

Event est recommandé pour la plupart des cas avec PHP-FPM. Worker est une alternative si event pose problème. Prefork est obligatoire uniquement si vous utilisez mod_php ou des modules non thread-safe. Si vous partez de zéro, choisissez event + PHP-FPM.

Mon .htaccess ne fonctionne pas, pourquoi ?

Vérifiez que AllowOverride All est configuré pour votre DocumentRoot dans la config Apache. Vérifiez aussi que mod_rewrite est activé (a2enmod rewrite). Enfin, vérifiez la syntaxe avec apachectl configtest et regardez les logs d’erreur.

Apache ou Nginx, lequel choisir ?

Les deux sont excellents. Nginx est généralement plus performant pour les fichiers statiques et comme reverse proxy. Apache est plus flexible avec .htaccess et mod_rewrite, et mieux supporté sur les hébergements mutualisés. Pour de nouvelles installations, Nginx est souvent préféré. Pour des migrations ou des environnements existants, Apache reste un excellent choix.