Boucle de requêtes Apache : quand une image manquante sature un serveur

Introduction

Un simple fichier manquant peut déclencher une tempête silencieuse.
C’est ce qui s’est produit dans ce cas concret : une image absente, appelée en boucle par une application, a généré plus de 42 millions de requêtes en 24 heures, saturant le disque du serveur malgré une rotation des logs parfaitement fonctionnelle.

Cet article détaille toute la démarche d’analyse, les commandes exécutées, les mesures de mitigation appliquées, et les bonnes pratiques à retenir pour éviter qu’un incident similaire ne se reproduise.


1️⃣ Constat initial : un disque plein malgré logrotate

Lors d’un contrôle régulier, la commande suivante montre un espace disque critique :

df -h

Résultat :
le volume racine est à 97 % d’utilisation, avec un fichier Apache dépassant les 12 Go.

Vérification des fichiers de logs :

du -sh /var/log/apache2/* | sort -h
ls -lh /var/log/apache2/*.log*

On constate que le fichier access.log.1 est anormalement volumineux.


2️⃣ Analyse des logs Apache

Pour comprendre la cause, on inspecte les dernières lignes :

tail -n 100 /var/log/apache2/site_ssl.access.log.1

Les journaux révèlent des milliers d’erreurs 404 sur une image :
/Data/Vente/defaut.gif

On mesure ensuite la volumétrie :

awk '$9 ~ /^404$/{print $7}' /var/log/apache2/site_ssl.access.log.1 \
| sort | uniq -c | sort -nr | head -n 10

👉 Le résultat montre que 99,9 % des requêtes concernent cette même ressource manquante.


3️⃣ Identifier les IP à l’origine du problème

On cherche quelles adresses génèrent le plus de trafic :

awk '{print $1}' /var/log/apache2/site_ssl.access.log.1 \
| sort | uniq -c | sort -nr | head -n 20

Puis on filtre uniquement sur la ressource problématique :

awk '$7=="/Data/Vente/defaut.gif"{print $1}' /var/log/apache2/site_ssl.access.log.1 \
| sort | uniq -c | sort -nr | head -n 10

Une seule IP représente plus de 42 millions de requêtes, confirmant une boucle applicative.


4️⃣ Vérification du débit de requêtes

Pour estimer la charge par minute :

awk '{g=substr($4,2,17); c[g]++} END{for (g in c) print g, c[g]}' /var/log/apache2/site_ssl.access.log.1 \
| sort | tail -n 20

Le résultat montre plusieurs milliers de requêtes par seconde — un déni de service interne auto-généré.


5️⃣ Audit de logrotate

Vérifions la configuration :

cat /etc/logrotate.d/apache2

Extrait typique :

/var/log/apache2/*.log {
    daily
    rotate 14
    compress
    delaycompress
    notifempty
    create 640 root adm
    sharedscripts
}

📌 Problème identifié :
delaycompress laisse le fichier .log.1 non compressé pendant 24 h, ce qui suffit à saturer le disque si le trafic est excessif.


6️⃣ Diagnostic final

  • Disque racine saturé à 97 %
  • Fichier d’accès Apache de 12 Go
  • 42 millions de requêtes sur 24 h
  • Cause : image manquante appelée en boucle par l’application
  • Effet : explosion du volume de logs malgré rotation correcte

7️⃣ Solutions possibles

Solution 1 – Correction applicative (durable)

Corriger le code de l’application pour ne plus appeler defaut.gif en boucle.
C’est la seule solution pérenne : tant que la requête est émise, le problème peut revenir.


🧩 Solution 2 – Mesure de contournement Apache

A. Option 1 : Fournir une image valide (1×1 pixel)

Créer une image minimale pour répondre 200 OK et casser la boucle :

mkdir -p /var/www/html/Data/Vente

printf 'R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==' | base64 -d \
  > /var/www/html/Data/Vente/defaut.gif

chown www-data:www-data /var/www/html/Data/Vente/defaut.gif
chmod 0644 /var/www/html/Data/Vente/defaut.gif

curl -sI http://127.0.0.1/Data/Vente/defaut.gif

Résultat attendu :

HTTP/1.1 200 OK

L’application cessera alors ses tentatives, stoppant instantanément la croissance des logs.


B. Option 2 : Bloquer proprement et ne pas loguer

Pour une réponse maîtrisée et silencieuse côté serveur :

# /etc/apache2/conf-available/block-missing-image.conf

SetEnvIf Request_URI "^/Data/Vente/defaut.gif$" dontlog

<Location "/Data/Vente/defaut.gif">
    Require all granted
    Header always set Content-Length "0"
    Header always set Content-Type "text/plain; charset=utf-8"
    Redirect gone "/Data/Vente/defaut.gif"
</Location>

Adapter ensuite la ligne CustomLog du vhost pour ignorer les entrées marquées :

CustomLog /var/log/apache2/site_ssl.access.log combined env=!dontlog

Appliquer :

apachectl configtest
systemctl reload apache2

Résultat attendu :

HTTP/1.1 410 Gone

8️⃣ Libération immédiate d’espace disque

# Compression du fichier volumineux
gzip /var/log/apache2/site_ssl.access.log.1

# Suppression si plus utile
rm /var/log/apache2/site_ssl.access.log.1

# Vérification
df -h

9️⃣ Ajustement de logrotate

Pour compresser les logs immédiatement après rotation, retirer la directive delaycompress :

sed -i 's/^\(\s*\)delaycompress/# \1delaycompress/' /etc/logrotate.d/apache2

Test de configuration :

logrotate --debug /etc/logrotate.d/apache2

Forcer une rotation :

logrotate -f /etc/logrotate.d/apache2

🔟 Vérification du retour à la normale

Contrôler que la ressource ne provoque plus de 404 :

curl -sI http://127.0.0.1/Data/Vente/defaut.gif

Observer le log en temps réel :

watch -n 2 "ls -lh /var/log/apache2/site_ssl.access.log; tail -n 3 /var/log/apache2/site_ssl.access.log"

Le fichier ne doit plus grossir rapidement.


1️⃣1️⃣ Correctif applicatif (à planifier)

Le problème ne sera définitivement éliminé que lorsque l’application cessera d’appeler en boucle la ressource manquante.
Recommandations :

  • livrer une image par défaut intégrée au code,
  • ou implémenter une gestion d’erreur avec backoff (temporisation entre tentatives),
  • ou ignorer les erreurs de ressource statique manquante.

1️⃣2️⃣ Bonnes pratiques et prévention

  1. Surveiller la taille des logs Apache quotidiennement.
  2. Configurer logrotate sans delaycompress pour compresser sans délai.
  3. Centraliser les logs volumineux sur un serveur externe (ELK, Loki, etc.).
  4. Créer une alerte sur la taille des fichiers .log.1 :
cat >/usr/local/sbin/check-apache-logsize.sh <<'EOS'
#!/usr/bin/env bash
THRESHOLD=$((1024*1024*1024))
for f in /var/log/apache2/*.log.1; do
  [ -f "$f" ] || continue
  size=$(stat -c%s "$f")
  if [ "$size" -gt "$THRESHOLD" ]; then
    echo "ALERTE : $f dépasse $(numfmt --to=iec $size)" | logger -t apache-logwatch
  fi
done
EOS

chmod +x /usr/local/sbin/check-apache-logsize.sh
echo '*/15 * * * * root /usr/local/sbin/check-apache-logsize.sh' > /etc/cron.d/apache-logwatch

1️⃣3️⃣ Résumé post-incident

ÉlémentDétail
CauseBoucle d’appels sur une image manquante (defaut.gif)
ImpactFichier de log Apache de 12 Go, disque saturé à 97 %
Solution temporaireHTTP 410 + exclusion de log ou image 1×1 px
Solution durableCorrection du code applicatif
PréventionAlerte sur taille de logs + logrotate immédiat

🧠 Conclusion

Cet incident illustre un cas classique où une anomalie applicative minime entraîne un incident d’infrastructure majeur.
Grâce à une analyse structurée, quelques commandes bien ciblées et une mesure de contournement simple, le service a été stabilisé en moins d’une heure.


🔎 À retenir

  • Une 404 répétée = un DoS interne.
  • Toujours vérifier la rotation ET le volume réel des logs.
  • Mettre en place un monitoring préventif.
  • La vraie solution reste toujours applicative.

🔖 Mots-clés SEO

apache2, logrotate, erreur 404, boucle de requêtes, image manquante, HTTP 410 Gone, fichier de log saturé, saturation disque, débogage apache, rotation des logs, maintenance serveur, serveur plein, analyse log Apache.