Credit : Logo officiel
Diagnostiquer les erreurs 500 sur un hébergement mutualisé
Diagnostiquer les erreurs 500 sur un hébergement mutualisé
Lundi matin, ticket P1 sur ma file : un client paniqué, sa boutique PrestaShop renvoie un mur blanc avec « Internal Server Error » sur toutes les pages depuis 6h du matin. Aucune modification de sa part, aucun déploiement. Sur les tickets que je traite chaque jour chez l'hébergeur, l'erreur 500 reste le motif numéro un — devant les soucis de mail et les certificats SSL. Et c'est aussi la plus mal documentée côté client, parce que par défaut le serveur ne dit rien d'utile : juste « 500 ». Voici la méthode que j'applique systématiquement, dans cet ordre précis, et qui résout 95 % des tickets en moins de 15 minutes.
Comprendre ce qu'est vraiment une 500
Le code HTTP 500 signifie « le serveur a essayé de générer ta page mais quelque chose côté serveur a planté avant la fin ». Sur un mutualisé typique IONOS, OVH ou Hostinger, la chaîne ressemble à ça :
Navigateur -> Apache (ou Nginx) -> PHP-FPM -> ton script PHP -> MySQL
La 500 peut venir de n'importe quel maillon, mais sur mutualisé 90 % du temps c'est le maillon PHP-FPM ou la couche Apache (.htaccess). Pas MySQL — un souci MySQL renvoie en général une page applicative avec un message, pas une 500 sèche.
Si tu reçois une 503, c'est différent : surcharge ou maintenance. Pour le 502, j'en parle dans Résoudre les erreurs 502 Bad Gateway Nginx.
Deuxième point important : sur mutualisé tu n'as pas accès à /var/log/apache2/error.log directement. L'hébergeur isole les logs par compte. Ton job, c'est de forcer PHP à logger là où tu peux y accéder — typiquement dans /home/utilisateur/logs/ ou via le panneau client.
Étape 1 : forcer PHP à parler
C'est l'étape qui résout le plus de tickets, et pourtant celle que les clients sautent. Tant que PHP ne te dit pas ce qui plante, tu tournes en rond.
À la racine de ton site (généralement /htdocs/ chez IONOS, /www/ chez OVH, /public_html/ chez Hostinger), crée ou édite un fichier .user.ini :
error_reporting = E_ALL
log_errors = On
error_log = /home/htdocs/logs/php-errors.log
display_errors = Off
html_errors = Off
log_errors_max_len = 0
Quelques règles que j'ai appris à la dure :
- Jamais
display_errors = Onen production. Tu exposes des chemins absolus, des mots de passe DB en cas de PDO mal try/catch, et tu offres une cartographie de ton serveur aux scripts de scan. - Le chemin de
error_logdoit être absolu et hors webroot. Si tu mets le log dans/htdocs/php-errors.log, il sera téléchargeable par n'importe qui. .user.inin'est pas relu instantanément. PHP-FPM le recharge toutes les 300 secondes par défaut (user_ini.cache_ttl). Patiente 5 minutes ou redémarre via le panneau si l'option existe.
Une fois le fichier en place, recharge la page qui plante puis :
ssh utilisateur@accesstoshell.ionos.com
tail -f /home/htdocs/logs/php-errors.log
Tu vas voir apparaître l'erreur réelle. Exemple typique :
[24-Apr-2026 09:14:22 UTC] PHP Fatal error: Uncaught Error: Class "Redis" not found in /htdocs/wp-content/object-cache.php:42
Là tu sais : extension Redis pas dispo sur le pack mutualisé. Solution : retirer object-cache.php ou demander un upgrade vers une offre avec Redis.
Si tu ne sais pas où chercher tes logs sur ton hébergeur, va lire Les logs Linux : où chercher et comment les lire — j'y détaille la structure de /var/log et les emplacements typiques par distribution.
Étape 2 : isoler le .htaccess
Deuxième cause la plus fréquente sur mes tickets : un .htaccess cassé. Le truc vicieux, c'est qu'une 500 .htaccess ne logge rien dans le PHP error log, parce que PHP n'est jamais appelé. C'est Apache qui rejette la requête en amont.
Le test diagnostique en 30 secondes :
cd /htdocs
mv .htaccess .htaccess.bak
curl -I https://tonsite.fr/
Si tu obtiens HTTP/2 200 ou même un 404 (mais plus de 500), le coupable est dedans. Remets-le et on chasse la directive fautive.
Les coupables récurrents que je vois :
php_value et php_flag avec PHP-FPM
Énorme classique. Quelqu'un copie un tuto de 2014 et colle :
php_value memory_limit 256M
php_value upload_max_filesize 64M
php_flag display_errors off
Ca ne marche que avec mod_php. Tous les mutualisés sérieux tournent en PHP-FPM/FastCGI depuis 2018, donc Apache hurle :
[Fri Apr 24 09:22:11.123456 2026] [core:alert] [pid 12345] [client 1.2.3.4] /htdocs/.htaccess: Invalid command 'php_value', perhaps misspelled or defined by a module not included in the server configuration
Fix : déplace tout dans .user.ini (syntaxe memory_limit = 256M, sans php_value).
Directive Apache 2.2 sur Apache 2.4
Autre classique post-migration :
# Ancienne syntaxe (Apache 2.2) - casse en 2.4
Order allow,deny
Allow from all
La nouvelle :
# Apache 2.4
Require all granted
Caractères invisibles BOM
Un client édite son .htaccess dans Word ou Notepad, qui ajoute un BOM UTF-8 (EF BB BF) en tête de fichier. Apache reçoit trois octets parasites avant la première directive et renvoie 500. Vérifie :
hexdump -C .htaccess | head -1
# 00000000 ef bb bf 52 65 77 72 69 74 65 45 6e 67 69 6e 65 |...RewriteEngine| <- mauvais
# 00000000 52 65 77 72 69 74 65 45 6e 67 69 6e 65 20 4f 6e |RewriteEngine On| <- bon
Fix rapide :
sed -i '1s/^\xEF\xBB\xBF//' .htaccess
mod_rewrite pas chargé
Rare sur les hébergeurs grand public mais ça arrive. Test :
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^test$ /index.php [L]
</IfModule>
Le <IfModule> évite la 500 si le module est absent. Toujours wrapper les directives optionnelles comme ça.
Étape 3 : la mémoire PHP
Symptôme caractéristique : la home charge, mais l'admin WordPress ou une page lourde renvoie 500. Ouvre ton php-errors.log et cherche :
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes) in /htdocs/wp-includes/class-wp-query.php on line 3211
134217728 octets = 128 Mo, le défaut PHP. Pousse-le dans .user.ini :
memory_limit = 512M
Pour WordPress, ajoute aussi dans wp-config.php avant le commentaire « That's all, stop editing! » :
define('WP_MEMORY_LIMIT', '512M');
define('WP_MAX_MEMORY_LIMIT', '512M');
Attention : sur les offres mutualisées d'entrée de gamme, le memory_limit est plafonné côté infra (souvent à 256 Mo). Tu peux mettre memory_limit = 4G, ça sera ignoré. Vérifie avec un phpinfo() temporaire ou via le panneau client.
Si ton site est WordPress et que tu as une page blanche plutôt qu'une vraie 500, l'approche est légèrement différente — j'ai détaillé ça dans Déboguer un site WordPress qui ne charge plus.
Étape 4 : permissions et propriétaire
Sur mutualisé, les permissions sont casse-pieds parce que ton compte tourne sous un utilisateur précis (souvent quelque chose comme u123456789) et que le moindre fichier en mauvais mode plante PHP.
Les valeurs canoniques :
| Type | Mode | Usage |
|---|---|---|
| Fichier PHP | 644 | Lisible par tous, écriture owner |
| .htaccess | 644 (ou 604 sur IONOS) | Idem |
| Répertoire | 755 | Traversable |
| wp-config.php / config inc | 600 ou 640 | Secrets DB |
| Upload écrivable | 755 ou 775 | Jamais 777 |
Pour auditer :
find /htdocs -type f -perm /o+w
Tout fichier remonté ici est world-writable, c'est un risque sécurité et ça peut déclencher des refus de PHP-FPM en mode strict (option security.limit_extensions qui refuse d'exécuter un PHP non possédé par le bon UID).
Corriger en masse :
find /htdocs -type f -exec chmod 644 {} \;
find /htdocs -type d -exec chmod 755 {} \;
chmod 600 /htdocs/wp-config.php
Pour creuser le sujet, va voir Résoudre les problèmes de permissions fichiers sous Linux : j'y compare les contraintes mutualisé vs dédié et je donne les commandes find adaptées à chaque cas.
Étape 5 : la version de PHP
Un client en PHP 7.4 qui passe en PHP 8.2 sans tester voit son site exploser. Symptômes typiques dans les logs :
PHP Fatal error: Uncaught Error: Call to undefined function each() in /htdocs/legacy/lib.php:88
PHP Fatal error: Uncaught Error: Class "Mongo" not found
PHP Deprecated: Required parameter $foo follows optional parameter $bar
each(), create_function(), mysql_* (sans i), les chaînes ${var} — tout ça a disparu en PHP 8.
Sur IONOS, OVH ou Hostinger, tu changes la version PHP depuis le panneau sans support :
- IONOS : Hébergement -> PHP -> sélectionner la version
- OVH : Espace client -> Hébergements -> Multisites -> modifier la version PHP par site
- Hostinger : hPanel -> Avancé -> Configuration PHP
Ma stratégie en cas de doute : tu repasses temporairement sur la version qui marchait, tu vérifies que c'est bien ça, puis tu fais évoluer le code progressivement. Pour savoir ce qui a changé en PHP 8.3, PHP 8.3 : les nouveautés qui changent tout liste les breaking changes.
Étape 6 : timeouts et workers saturés
Si la 500 arrive après exactement 30 secondes ou 60, c'est un timeout.
Deux timeouts à connaître :
; .user.ini
max_execution_time = 120
max_input_time = 120
Mais sur mutualisé il existe aussi un timeout côté PHP-FPM (request_terminate_timeout) que tu ne contrôles pas. Si ton script dépasse, tu auras dans le log Apache :
[proxy_fcgi:error] [pid 23456] (70007)The timeout specified has expired: [client 1.2.3.4] AH01075: Error dispatching request to :
Ca, c'est PHP-FPM qui a tué le process. Solution : optimiser le script (cache, requêtes SQL groupées) ou passer sur du CLI/cron pour les tâches longues plutôt que via HTTP.
Second piège classique : trop de connexions FTP simultanées. FileZilla configuré à 10 transferts en parallèle, plus 2 navigateurs ouverts, plus un cron qui tourne — sur un pack à 5 workers PHP, tu satures. La 500 apparaît aléatoirement, en intermittent. Réduis FileZilla à 2-3 connexions max :
Édition -> Paramètres -> Transferts -> Nombre maximum de transferts simultanés : 2
Étape 7 : vérifier que ce n'est pas un module manquant
Sur les mutualisés économiques, certaines extensions PHP ne sont pas activées. Crée un fichier _phpcheck.php (à supprimer après !) :
<?php
header('Content-Type: text/plain');
echo 'PHP ' . PHP_VERSION . "\n\n";
foreach (['curl','mbstring','intl','gd','imagick','redis','memcached','pdo_mysql','zip','soap'] as $ext) {
echo str_pad($ext, 15) . (extension_loaded($ext) ? 'OK' : 'MANQUANT') . "\n";
}
Les manquants typiques qui font crasher un CMS : intl (Symfony, PrestaShop), imagick (WordPress images), soap (intégrations B2B).
Erreurs courantes et leur fix
Voici les messages exacts que je vois passer chaque semaine, et le fix associé.
1) PHP Fatal error: Allowed memory size of 134217728 bytes exhausted
Monte memory_limit à 256M ou 512M dans .user.ini. Si plafonné par l'hébergeur, allège le code (désactiver plugin lourd, paginer les requêtes).
2) .htaccess: Invalid command 'php_value'
Migre les directives php_value / php_flag du .htaccess vers .user.ini au format clé = valeur.
3) PHP Fatal error: Uncaught Error: Call to undefined function mysql_connect()
Code legacy en PHP 7+/8. Soit tu rebascules en PHP 5.6 temporairement (rare et risqué), soit tu migres vers mysqli ou PDO. La fonction n'existe plus depuis PHP 7.0.
4) proxy_fcgi:error AH01075: Error dispatching request
Timeout PHP-FPM dépassé. Optimise le script ou déporte la tâche en cron CLI. Augmenter max_execution_time ne suffit pas, le request_terminate_timeout côté FPM est prioritaire.
5) RewriteEngine: not allowed here ou mod_rewrite: not loaded
Wrappe tes règles dans <IfModule mod_rewrite.c>...</IfModule>. Si le module est vraiment absent, contacte le support — sur un pack moderne il est toujours dispo, sinon tu n'es plus sur le bon plan.
6) Page 500 sans aucune ligne dans le log PHP
C'est presque toujours Apache qui rejette avant PHP : .htaccess cassé, fichier interdit (mode 000), ou propriétaire incorrect. Renomme .htaccess, vérifie stat -c '%U:%G %a' index.php.
Pour aller plus loin
- Les logs Linux : où chercher et comment les lire — la structure de
/var/log, journalctl et lecture des logs Apache/Nginx. - Déboguer un site WordPress qui ne charge plus — méthode spécifique WordPress, plugins fautifs, mode debug.
- Résoudre les problèmes de permissions fichiers sous Linux — chmod, chown et les pièges du mutualisé.
- Résoudre les erreurs 502 Bad Gateway Nginx — le cousin de la 500 quand tu es derrière Nginx.
- PHP 8.3 : les nouveautés qui changent tout — pour anticiper les breaking changes avant de monter de version.
Ce que je retiens après 5 ans de tickets
La 500 fait peur parce qu'elle est silencieuse, mais 95 % des cas tombent dans cinq seaux : log PHP qui n'est pas activé, .htaccess invalide, mémoire insuffisante, mauvaise version de PHP, permissions cassées. Active toujours le log PHP avant de toucher à quoi que ce soit, lis le message exact, et règle un seul paramètre à la fois — tu gagneras un temps fou par rapport à la méthode « je change tout en même temps ». Et garde en tête qu'un mutualisé est un environnement contraint : si tu te retrouves à pousser memory_limit à 1 Go ou à attendre 90 secondes une page, c'est probablement le moment de regarder un VPS.