Une erreur 500 sur admin-ajax.php signifie que PHP plante pendant le traitement d’une requête AJAX WordPress : dans l’immense majorité des cas, un plugin ou un thème dont le hook wp_ajax_* déclenche une erreur fatale. La méthode de réparation tient en trois gestes : reproduire la requête avec curl, activer WP_DEBUG pour capter l’erreur exacte dans debug.log, puis désactiver les extensions une à une avec WP-CLI jusqu’à isoler la coupable. Les autres causes classiques — memory_limit épuisé, timeout PHP-FPM, pare-feu ModSecurity, API Heartbeat trop bavarde — se diagnostiquent avec le même arbre de décision. Suivez-le dans l’ordre : vous saurez d’où vient le problème en moins de trente minutes.

Où l’erreur apparaît : lisez les symptômes avant de toucher au code

Le fichier /wp-admin/admin-ajax.php est le point d’entrée historique de toutes les requêtes AJAX de WordPress, côté administration comme côté public. Une erreur 500 dessus peut donc se manifester à quatre endroits différents, et chacun oriente le diagnostic :

  • Console du navigateur : vous voyez POST https://votre-site.fr/wp-admin/admin-ajax.php 500 ou admin-ajax.php failed to load resource. Ouvrez l’onglet Réseau, cliquez sur la requête en rouge et regardez l’onglet « Charge utile » : le paramètre action vous donne le nom du hook fautif. C’est l’indice le plus précieux de tout le dépannage.
  • Logs serveur (error.log Apache ou Nginx, journal PHP-FPM) : cherchez les lignes contenant admin-ajax.php. Un PHP Fatal error pointe un plugin, un upstream timed out pointe un timeout PHP-FPM, un Allowed memory size exhausted pointe la mémoire.
  • Google Search Console : des erreurs 500 sur admin-ajax.php dans le rapport de couverture signalent presque toujours un plugin qui appelle AJAX côté front (panier, compteur de vues, filtre de recherche). Google ne devrait pas explorer cette URL : le problème existe donc aussi pour vos visiteurs.
  • Monitoring uptime : si votre sonde teste une page qui charge admin-ajax.php au rendu, les alertes intermittentes traduisent souvent une surcharge (Heartbeat, pic de trafic) plutôt qu’un bug permanent.

Si tout le site renvoie des 500 et pas seulement admin-ajax.php, le problème est plus large : suivez d’abord notre guide sur l’erreur 500 WordPress générale, puis revenez ici si l’erreur persiste uniquement sur les appels AJAX.

Étape 1 : reproduire la requête avec curl

Ne dépannez jamais à l’aveugle depuis le navigateur : le cache, les cookies et les extensions parasitent l’observation. Reproduisez la requête proprement en ligne de commande. Commencez par l’action heartbeat, présente sur toute installation WordPress :

curl -i -X POST https://votre-site.fr/wp-admin/admin-ajax.php 
  -d "action=heartbeat"

# Puis testez l'action exacte relevée dans la console :
curl -i -X POST https://votre-site.fr/wp-admin/admin-ajax.php 
  -d "action=mon_action_suspecte" 
  -H "User-Agent: Mozilla/5.0"

Interprétez le premier chiffre de la réponse : un 200 avec le corps 0 est normal (action inconnue ou non connectée, WordPress répond « zéro »), un 400 aussi sur les versions récentes. Un 500 confirme le plantage PHP. Un 403 déguise souvent un blocage WAF ou ModSecurity — on y revient plus bas. Si heartbeat passe mais que votre action spécifique plante, vous tenez déjà le périmètre : c’est le code accroché à ce hook précis qui casse, pas le serveur.

Étape 2 : activer WP_DEBUG et capter l’erreur exacte

Une erreur 500 est un aveu d’ignorance du serveur : il faut forcer PHP à écrire la vraie erreur quelque part. Éditez wp-config.php et placez ces lignes avant le commentaire « C’est tout, ne touchez pas à ce qui suit » :

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );      // écrit dans wp-content/debug.log
define( 'WP_DEBUG_DISPLAY', false ); // n'affiche rien aux visiteurs
@ini_set( 'display_errors', 0 );

Rejouez ensuite votre commande curl, puis lisez la fin du journal :

tail -n 40 wp-content/debug.log

La ligne PHP Fatal error: Uncaught Error ... in /wp-content/plugins/nom-du-plugin/... désigne directement le fichier coupable. Notez le chemin : le nom du dossier après /plugins/ est le plugin à désactiver. Si le journal reste vide alors que le 500 persiste, l’erreur survient avant même que WordPress ne se charge (PHP-FPM tué, mémoire, WAF) : passez aux causes serveur.

Étape 3 : identifier le hook AJAX fautif

Chaque requête AJAX WordPress porte un paramètre action, et chaque action correspond à un hook wp_ajax_{action} (utilisateur connecté) ou wp_ajax_nopriv_{action} (visiteur anonyme). Pour retrouver quel code répond à l’action relevée dans la console, un simple grep suffit :

grep -rn "wp_ajax_mon_action_suspecte" wp-content/plugins wp-content/themes

Le résultat vous dit immédiatement quel plugin ou quel thème enregistre le handler. Croisez avec debug.log : si les deux pointent le même dossier, le diagnostic est terminé, il ne reste que la correction. Cas fréquent après une mise à jour : le plugin appelle une fonction supprimée d’une bibliothèque, ou le thème enfant surcharge un fichier devenu incompatible. En attendant un correctif de l’auteur, désactivez l’extension ou revenez à la version précédente.

Cause n°1 : un plugin ou un thème cassé, souvent après une mise à jour

C’est la cause dominante. Si debug.log ne vous a pas donné le nom directement, isolez par dichotomie avec WP-CLI — plus rapide et plus sûr que la manipulation en base de données, surtout si wp-admin ne fonctionne pas non plus :

# Sauvegarder la liste des plugins actifs
wp plugin list --status=active --field=name > plugins-actifs.txt

# Tout désactiver
wp plugin deactivate --all

# Tester : le 500 a disparu ? Un plugin est en cause.
curl -i -X POST https://votre-site.fr/wp-admin/admin-ajax.php -d "action=heartbeat"

# Réactiver un par un et retester après chacun
wp plugin activate woocommerce
wp plugin activate wordpress-seo
# ... jusqu'à ce que le 500 réapparaisse : le dernier activé est le fautif

Si le 500 persiste avec tous les plugins désactivés, testez le thème : wp theme activate twentytwentyfive. Le 500 disparaît ? Le coupable est dans le functions.php de votre thème. Pensez aussi à vider le cache entre chaque test : un cache de page ou d’objet peut vous faire croire à une correction — ou à une panne — qui n’existe plus.

Cause n°2 : memory_limit épuisé

Le message Allowed memory size of X bytes exhausted dans les logs est sans ambiguïté. Les requêtes AJAX lourdes (import, constructeur de pages, recalcul de panier WooCommerce) consomment davantage que le chargement d’une page classique. Deux réglages à vérifier : la limite PHP elle-même et la limite interne de WordPress. Dans wp-config.php :

define( 'WP_MEMORY_LIMIT', '256M' );
define( 'WP_MAX_MEMORY_LIMIT', '512M' );

Côté serveur, vérifiez la valeur réelle avec php -i | grep memory_limit ou dans l’outil « Santé du site » de l’administration (voyez comment accéder à wp-admin si nécessaire). Augmenter la mémoire soulage, mais ne guérit pas : si un plugin dévore 512 Mo à chaque requête AJAX, c’est lui le problème, pas votre hébergeur. Profilez avant d’empiler les mégaoctets.

Cause n°3 : timeout PHP-FPM ou passerelle

Un 500 — parfois un 502 ou 504 selon la configuration — accompagné de upstream timed out ou Maximum execution time exceeded signale une requête AJAX trop longue. Trois compteurs entrent en jeu et le plus court gagne : max_execution_time de PHP (souvent 30 s), request_terminate_timeout de PHP-FPM, et fastcgi_read_timeout (Nginx) ou ProxyTimeout (Apache). Alignez-les temporairement à 120 s pour confirmer le diagnostic, puis cherchez pourquoi la requête est lente : requête SQL sans index, appel d’API externe qui ne répond pas, boucle sur des milliers de produits. Une action AJAX qui dépasse quelques secondes est un bug de conception, pas un problème de timeout.

Cause n°4 : WAF, ModSecurity ou pare-feu d’hébergeur

Les pare-feu applicatifs adorent admin-ajax.php : c’est une cible réelle des bots, donc les règles sont agressives. ModSecurity, en particulier, peut renvoyer un 500 (et non un 403) quand une règle mal écrite plante. Indices typiques : l’erreur ne touche que certaines actions ou certains payloads (ceux qui contiennent du HTML, du JSON volumineux, des URL), elle disparaît en local, et debug.log reste vide puisque PHP n’est jamais exécuté. Vérifiez le journal modsec_audit.log ou demandez à votre hébergeur l’identifiant de la règle déclenchée, puis faites-la désactiver pour ce chemin précis plutôt que de couper tout le pare-feu. Même logique pour Cloudflare : consultez « Security > Events » et créez une exception ciblée, jamais une désactivation globale.

Cause n°5 : Heartbeat et admin-ajax utilisé côté front

L’API Heartbeat envoie une requête action=heartbeat toutes les 15 à 60 secondes par onglet d’administration ouvert. Multipliez par dix rédacteurs et quelques onglets oubliés, et un petit serveur mutualisé sature : les 500 deviennent intermittents et apparaissent aux heures de bureau. Réduisez la fréquence proprement, sans plugin supplémentaire, dans le functions.php du thème enfant :

add_filter( 'heartbeat_settings', function( $settings ) {
    $settings['interval'] = 60; // secondes, minimum 15
    return $settings;
} );

// Ou couper Heartbeat hors éditeur :
add_action( 'admin_init', function() {
    global $pagenow;
    if ( 'post.php' !== $pagenow && 'post-new.php' !== $pagenow ) {
        wp_deregister_script( 'heartbeat' );
    }
} );

Autre angle mort : beaucoup de plugins appellent admin-ajax.php sur les pages publiques — compteurs de vues, paniers « ajax add to cart », filtres de boutique, popups. Ces requêtes contournent tout cache de page : chaque visiteur frappe PHP directement. Si vos 500 arrivent lors des pics de trafic, identifiez ces appels dans l’onglet Réseau d’une page front, puis désactivez la fonctionnalité ou remplacez-la par une solution basée sur l’API REST avec mise en cache.

Rollback : remettez le site en configuration propre

Un dépannage n’est terminé que lorsque les rustines de diagnostic ont été retirées. Dans l’ordre :

  • Réactivez les plugins restés désactivés, sauf le fautif : wp plugin activate $(cat plugins-actifs.txt | grep -v plugin-fautif) ou un par un si vous préférez contrôler.
  • Retirez WP_DEBUG en production : repassez WP_DEBUG et WP_DEBUG_LOG à false, puis supprimez wp-content/debug.log, qui peut contenir des chemins serveur et des informations sensibles accessibles publiquement.
  • Restaurez les timeouts montés à 120 s pour le test : des valeurs hautes en permanence transforment chaque bug en attaque par épuisement de workers PHP-FPM.
  • Videz tous les caches (page, objet, CDN) pour servir la version réparée à tout le monde.
  • Notez la cause dans votre journal d’exploitation : le prochain 500 se diagnostiquera deux fois plus vite.

FAQ : admin-ajax.php

Peut-on bloquer complètement admin-ajax.php ?

Non, sauf à casser des fonctions essentielles. Contrairement à xmlrpc.php, admin-ajax.php sert à l’administration (Heartbeat, sauvegarde automatique) et à de nombreux plugins côté public. Vous pouvez en revanche le limiter : rate limiting sur le chemin, blocage des requêtes sans paramètre action, ou règles WAF ciblées sur les actions inconnues.

Pourquoi admin-ajax.php est-il lent ?

Parce que chaque appel charge tout WordPress : noyau, plugins actifs, thème. Une requête AJAX minimale coûte donc autant qu’une page non mise en cache. Si la lenteur vient d’un plugin précis, le profiling (Query Monitor) le montrera ; sinon, réduisez le nombre de plugins chargés ou migrez les endpoints critiques vers l’API REST.

Pourquoi admin-ajax.php renvoie-t-il 0 ou 400 ?

Ce n’est pas une erreur serveur. Le 0 (ou le 400 depuis WordPress 4.6+) signifie qu’aucun handler ne correspond à l’action envoyée : action mal orthographiée, hook wp_ajax_nopriv_* manquant pour les visiteurs non connectés, ou plugin désactivé. Seul le code 500 traduit un vrai plantage PHP.

Les erreurs 500 sur admin-ajax.php dans Search Console sont-elles graves pour le SEO ?

Indirectement. Google ne classe pas cette URL, mais des 500 fréquents révèlent un serveur en difficulté, ce qui dégrade l’exploration de tout le site. Corrigez la cause, laissez Disallow: /wp-admin/ avec l’exception Allow: /wp-admin/admin-ajax.php dans robots.txt, et les rapports se nettoieront seuls.

Sources et références

G
WP Admin Lab

Architecte web full-stack. WordPress, performance, data et sécurité. Notes de terrain, tests reproductibles et retours d'expérience.