Ce message ne désigne pas une extension défaillante. Il constate qu’un script a demandé de la mémoire alors qu’il avait déjà atteint la limite memory_limit de PHP. Cette limite, WordPress peut la relever mais jamais la baisser, et seulement si l’hébergeur le permet. La réparation se fait donc en deux temps : savoir quelle limite s’applique vraiment, puis trouver ce qui la remplit. Augmenter la mémoire fait disparaître le message. Cela ne dit rien de sa cause, qui reviendra si elle grossit avec le site.

La réponse en trois lignes

Divisez le premier nombre par 1 048 576 : 134217728 octets font 128 Mo. Ajoutez define( 'WP_MEMORY_LIMIT', '256M' ); dans wp-config.php, au-dessus de la ligne « C’est tout, ne touchez pas à ce qui suit », puis vérifiez dans Outils > Santé du site > Informations > Serveur que la limite a changé. Si elle n’a pas changé, l’hébergeur la verrouille : c’est dans son panneau qu’elle se règle. Ensuite, cherchez ce qui consomme.

Ce que dit le message, mot pour mot

Le texte vient de PHP, pas de WordPress. Dans le gestionnaire de mémoire de PHP (Zend/zend_alloc.c), il s’écrit ainsi :

Allowed memory size of %zu bytes exhausted (tried to allocate %zu bytes)

Le premier nombre est la limite en vigueur au moment de l’erreur, en octets. Le second est la taille de la demande que PHP a refusée. Suivent, dans le journal, le chemin du fichier et le numéro de ligne où cette demande a été faite. C’est une erreur fatale (E_ERROR) : le script s’arrête net, et le journal du serveur l’inscrit sous la forme PHP Fatal error: Allowed memory size of ….

Convertir les octets

PHP compte en octets, les réglages s’écrivent en mégaoctets. WordPress fait la conversion avec wp_convert_hr_to_bytes(), dans wp-includes/load.php, où M vaut MB_IN_BYTES, soit 1 024 × 1 024 = 1 048 576 octets. Les valeurs qu’on croise le plus :

Dans le messageRéglageD’où vient souvent cette valeur
4194304040MWP_MEMORY_LIMIT par défaut d’un site simple : PHP était réglé plus bas, et WordPress a relevé la limite jusque-là.
6710886464MWP_MEMORY_LIMIT par défaut d’un multisite, ou réglage d’hébergeur.
134217728128MValeur par défaut de memory_limit dans PHP : souvent, personne n’a rien réglé.
268435456256MWP_MAX_MEMORY_LIMIT par défaut : la limite que WordPress accorde à l’administration, aux images et au cron.
536870912512MLimite déjà relevée, à la main ou par l’hébergeur.
10737418241GLimite très haute : à ce niveau, cherchez une consommation qui ne s’arrête pas avant de monter encore.

« tried to allocate » ne désigne pas le coupable

Le second nombre mesure la dernière demande, celle qui a fait déborder. S’il est petit, quelques kilo-octets, la mémoire était déjà pleine : une demande minuscule a suffi, et ce qui compte est ce qui a rempli la mémoire avant elle. S’il est énorme, du même ordre que la limite, une seule opération voulait beaucoup d’un coup : une image décompressée, un fichier lu en entier, une requête qui ramène une table complète. Dans ce second cas, la ligne citée est plus parlante.

Une autre forme du message ne relève pas de ce guide : Out of memory (allocated … bytes) (tried to allocate … bytes). Là, ce n’est pas memory_limit qui a dit non, c’est le système, qui n’a plus de mémoire à donner au processus. Relever WP_MEMORY_LIMIT n’y change rien : c’est la mémoire du serveur ou du conteneur qui manque.

Pourquoi le fichier cité est souvent innocent

PHP signale l’endroit où l’allocation a échoué. Si une extension a rempli la mémoire pendant tout le chargement, la goutte qui fait déborder peut tomber n’importe où ensuite, et très souvent dans un fichier du cœur : wp-includes/…, au moment où WordPress fait son travail ordinaire. Désinstaller le fichier cité, ou réinstaller WordPress, ne sert à rien.

Cela pèse aussi sur le mode de récupération. Lu dans WordPress 7.1.3, WP_Fatal_Error_Handler::handle() intercepte bien les E_ERROR, et affiche « Il y a eu une erreur critique sur ce site. » si les en-têtes ne sont pas encore partis. Mais l’e-mail de récupération, « [Nom du site] Votre site connaît un problème technique », ne part que si WP_Recovery_Mode::handle_error() trouve un responsable. Sa méthode get_extension_for_error() ne regarde que le chemin du fichier de l’erreur : sous WP_PLUGIN_DIR, c’est une extension ; dans un dossier de thèmes, c’est un thème ; ailleurs, la réponse est « Error not caused by a plugin or theme. » et rien n’est envoyé. Une mémoire épuisée dans wp-includes ne met donc aucune extension en pause, même quand une extension en est la cause.

Deux autres conditions limitent cet e-mail : l’erreur doit survenir sur un point protégé, au sens de is_protected_endpoint() (l’administration hors Ajax, wp-login.php et quelques actions Ajax listées), et le site ne doit pas être un multisite. Une mémoire épuisée sur la page d’accueil ne déclenche aucun e-mail. Si les en-têtes étaient déjà partis, sur le site public, la page s’arrête au milieu sans message : c’est le cas décrit dans le guide de la page blanche.

Où lire le message complet

La page d’erreur critique ne montre ni les nombres ni le fichier. Trois endroits les donnent.

  • Le journal de WordPress. Dans wp-config.php, au-dessus de la ligne d’arrêt :
    define( 'WP_DEBUG', true );
    define( 'WP_DEBUG_LOG', true );
    define( 'WP_DEBUG_DISPLAY', false );
    Reproduisez l’erreur, puis ouvrez wp-content/debug.log. Depuis WordPress 5.1, WP_DEBUG_LOG accepte aussi un chemin de fichier, à placer hors de la racine publique.
  • Le journal d’erreurs du serveur. PHP y écrit ses erreurs fatales même sans WP_DEBUG, si log_errors est actif : fichier error_log à la racine, journal de PHP-FPM, ou rubrique « Logs » du panneau de l’hébergeur.
  • L’e-mail du mode de récupération, depuis WordPress 5.2, quand les conditions ci-dessus sont réunies. Il contient le message complet, le fichier et la ligne.

Quelle limite s’applique vraiment

Il n’y a pas une limite, il y en a trois, et c’est ce qui rend les conseils habituels trompeurs. Tout commence dans wp_initial_constants(), dans wp-includes/default-constants.php, que wp-settings.php appelle juste après la lecture de wp-config.php.

La limite du site public : WP_MEMORY_LIMIT

Si vous ne la définissez pas, WordPress choisit 40M, ou 64M en multisite. Puis il ne l’applique qu’à une condition :

$wp_limit_int = wp_convert_hr_to_bytes( WP_MEMORY_LIMIT );
if ( -1 !== $current_limit_int && ( -1 === $wp_limit_int || $wp_limit_int > $current_limit_int ) ) {
	ini_set( 'memory_limit', WP_MEMORY_LIMIT );
}

WordPress relève, il ne baisse jamais. Avec les réglages par défaut, 40M étant inférieur aux 128M de PHP, il ne fait rien du tout : c’est la limite de PHP qui s’applique. Et si PHP est déjà illimité (-1), WordPress n’y touche pas non plus.

Avant de choisir ces valeurs, WordPress demande à wp_is_ini_value_changeable( 'memory_limit' ) si le réglage peut être modifié en cours d’exécution. La fonction lit ini_get_all() et ne répond oui que si l’accès au réglage est INI_ALL ou INI_USER. Quand l’hébergeur fixe la valeur avec php_admin_value, dans la configuration d’Apache ou du pool PHP-FPM, elle n’est plus modifiable par le script : WP_MEMORY_LIMIT prend alors la valeur en place, et une valeur plus haute écrite dans wp-config.php reste lettre morte, sans le moindre avertissement, car l’échec de ini_set() n’est pas signalé.

La limite de l’administration : WP_MAX_MEMORY_LIMIT

Par défaut 256M, sauf si PHP est déjà au-dessus (WordPress garde alors la valeur de PHP) ou si WP_MEMORY_LIMIT dépasse 256 Mo (il la reprend). Dans wp-admin/admin.php, la hausse est conditionnelle :

if ( current_user_can( 'manage_options' ) ) {
	wp_raise_memory_limit( 'admin' );
}

Seuls les comptes qui ont manage_options, les administrateurs, en profitent. Un éditeur ou un auteur travaille dans l’administration avec la limite du site public. C’est l’explication d’un cas qui déroute : l’erreur frappe un éditeur, et l’administrateur ne la reproduit pas.

Les limites des images et du cron

wp_raise_memory_limit(), dans wp-includes/functions.php, existe depuis WordPress 4.6. Elle sort tout de suite si la limite n’est pas modifiable ou si PHP est illimité, ne relève jamais en dessous de la valeur actuelle, et passe la valeur cible dans un filtre propre à chaque contexte :

  • admin : filtre admin_memory_limit ;
  • image : filtre image_memory_limit, appelé au chargement d’une image par WP_Image_Editor_GD et WP_Image_Editor_Imagick, quel que soit le rôle de la personne qui envoie le fichier ;
  • cron : filtre cron_memory_limit, depuis WordPress 6.3, appelé par wp-cron.php ;
  • tout autre nom : filtre {$context}_memory_limit, que les extensions utilisent pour leurs propres traitements.

Toutes ces hausses passent par ini_set(). Elles butent donc sur le même verrou que WP_MEMORY_LIMIT : si l’hébergeur a figé memory_limit, aucune n’a d’effet.

Lire la limite réelle

Dans Outils > Santé du site > Informations, la section Serveur affiche « Limite de mémoire PHP ». La valeur est lue par WP_Site_Health, que wp-settings.php instancie après wp_initial_constants() : c’est la limite du site public, WP_MEMORY_LIMIT compris. Si l’administration a relevé la limite, une seconde ligne apparaît : « Limite de mémoire PHP (uniquement pour les écrans d’administration) ». La section Constantes WordPress affiche, elle, WP_MEMORY_LIMIT et WP_MAX_MEMORY_LIMIT telles qu’elles sont définies.

La comparaison des deux sections suffit au diagnostic : une constante à 256M et une limite PHP restée à 128M veulent dire que ini_set() a été refusé, donc que la limite se règle chez l’hébergeur. La même section Serveur donne aussi « PHP SAPI », utile plus bas.

Avec WP-CLI :

wp eval 'echo ini_get("memory_limit");'

Attention : WP-CLI lance PHP en ligne de commande, qui lit souvent un autre php.ini que le serveur web. La valeur obtenue est celle des tâches lancées par WP-CLI, pas forcément celle des visiteurs. Pour le site, la Santé du site fait foi.

Augmenter la limite, dans l’ordre

1. wp-config.php

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

/* C’est tout, ne touchez pas à ce qui suit ! Bonne publication. */

L’emplacement compte. Juste sous cette ligne, wp-config.php charge wp-settings.php, qui appelle wp_initial_constants() et définit la constante si elle n’existe pas. Un define() placé après arrive trop tard : la constante existe déjà, PHP émet un avertissement « Constant WP_MEMORY_LIMIT already defined » et garde la première valeur. Ajoutez define( 'WP_MAX_MEMORY_LIMIT', '512M' ); seulement si l’erreur touche l’administration, les images ou le cron et que 256 Mo ne suffisent pas. Cette méthode marche quand l’hébergeur laisse memory_limit modifiable ; la Santé du site le dit.

2. php.ini ou .user.ini

memory_limit = 256M

Avec un accès au serveur, dans le php.ini utilisé par le serveur web, puis redémarrage de PHP-FPM. Sur un hébergement mutualisé en PHP-FPM ou FastCGI, dans un fichier .user.ini à la racine du site : PHP le relit à intervalle régulier (user_ini.cache_ttl, 300 secondes par défaut), la nouvelle valeur peut donc mettre quelques minutes à apparaître. Ce fichier ne lève pas un verrou posé par php_admin_value.

3. .htaccess, seulement avec mod_php

php_value memory_limit 256M

Cette directive n’existe que si PHP tourne comme module d’Apache : la ligne « PHP SAPI » de la Santé du site affiche alors apache2handler. Avec fpm-fcgi ou cgi-fcgi, Apache ne la connaît pas et répond par une erreur 500 sur tout le site. Retirez-la aussitôt si c’est le cas.

4. Le panneau de l’hébergeur

Quand la Santé du site montre une limite qui ne bouge pas malgré wp-config.php et .user.ini, la valeur est fixée par l’hébergeur. Elle se change dans son panneau (réglages PHP, sélecteur de version, options du pool) ou par son support. Rien de ce que WordPress ou une extension écrit ne la dépassera.

Pour la valeur, 256M est celle que WordPress accorde lui-même à son administration : c’est un repère raisonnable pour le site public. Au-delà, chaque palier gagné sans comprendre la cause masque un peu plus le problème.

Trouver ce qui consomme

Le test le plus simple est la hausse elle-même. Si l’erreur revient avec le nouveau nombre dans le message (268435456 au lieu de 134217728), la consommation grandit avec la place qu’on lui donne : une boucle, une requête sans limite, un traitement qui charge tout. Aucune valeur ne suffira. Si l’erreur disparaît et ne revient pas, il manquait seulement de la place.

Relier l’erreur à une action

L’heure de chaque ligne de debug.log se compare à ce qui se passait : un import, une sauvegarde lancée par une extension, la régénération des miniatures, l’ouverture d’un constructeur de pages, une tâche du cron. Une erreur qui tombe à heure fixe, sans action de personne, désigne d’abord le cron, qui a sa propre limite (cron ci-dessus).

Écarter les extensions

Après une sauvegarde, désactivez les extensions, puis réactivez-les par moitiés jusqu’à isoler celle qui ramène l’erreur. Sans accès à l’administration, renommez wp-content/plugins le temps du test, ou passez par WP-CLI :

wp plugin list --status=active
wp plugin deactivate nom-de-l-extension
wp --skip-plugins eval 'echo memory_get_peak_usage( true );'

La dernière commande charge WordPress sans aucune extension et affiche son pic de mémoire en octets. Relancée sans --skip-plugins, elle donne l’écart que les extensions ajoutent au chargement, sur la même machine. Pensez aussi aux extensions obligatoires du dossier wp-content/mu-plugins, que la liste habituelle n’affiche pas et que la désactivation n’atteint pas.

Mesurer avec Query Monitor

L’extension Query Monitor affiche, pour chaque page qui se termine, le pic de mémoire atteint et sa part de la limite. Elle ne voit pas la requête qui plante, puisque celle-ci meurt avant la fin, mais elle montre les pages qui en approchent. Comparez le même écran avec et sans l’extension suspecte, et regardez dans son panneau des requêtes celles qui ramènent un nombre de lignes anormal.

Les images

Pour créer les tailles intermédiaires, WordPress décompresse l’image d’origine. Le code de WP_Image_Editor_GD le dit en commentaire : GD garde l’image non compressée en mémoire. La mémoire nécessaire dépend donc du nombre de pixels, pas du poids du fichier : une photo de quelques mégaoctets peut exiger beaucoup plus une fois décompressée. Si l’erreur suit l’envoi d’une photo, « Éditeur actif », dans la section « Traitement des médias » de la Santé du site, dit lequel des deux éditeurs travaille. Réduire les dimensions avant l’envoi règle le problème à la source.

Les options chargées à chaque page

wp_load_alloptions() charge à chaque requête toutes les options marquées pour le chargement automatique. Des extensions y stockent parfois des journaux ou des caches entiers, et chaque page paie ce poids. Depuis WordPress 6.6, la Santé du site signale « Les options chargées automatiquement peuvent affecter les performances » au-delà de 800 000 octets (filtre site_status_autoloaded_options_size_limit). Pour voir les plus lourdes :

SELECT option_name, LENGTH(option_value) AS octets
FROM wp_options
WHERE autoload IN ('yes', 'on', 'auto-on', 'auto')
ORDER BY octets DESC LIMIT 20;

Ces quatre valeurs sont celles que wp_autoload_values_to_autoload() retient. Le nom de l’option désigne en général l’extension qui l’a écrite. Remplacez wp_ par votre préfixe.

Ce qui ne marche pas

Monter à -1 ou à 1G « pour être tranquille ». Une boucle consomme ce qu’on lui donne. Le message change, ou devient un « Out of memory » quand le serveur n’a plus rien, ou un dépassement du temps d’exécution ; la cause est intacte.

Mettre WP_MEMORY_LIMIT plus bas pour économiser. WordPress ne baisse jamais la limite de PHP : la ligne est ignorée.

Relever seulement WP_MAX_MEMORY_LIMIT quand l’erreur touche le site public. Cette constante ne sert qu’à l’administration des comptes qui ont manage_options, aux images, au cron et aux contextes des extensions.

Réinstaller WordPress parce que le fichier cité est dans wp-includes. Ce fichier est l’endroit où la mémoire a manqué, pas celui qui l’a prise.

Vérifier que c’est terminé

La Santé du site doit afficher la nouvelle limite dans la section Serveur. Refaites l’action qui déclenchait l’erreur, avec le même compte (un administrateur n’a pas la même limite qu’un éditeur), et vérifiez qu’aucune ligne n’apparaît dans debug.log. Puis retirez les trois lignes de débogage de wp-config.php. Si la page affiche encore « Il y a eu une erreur critique sur ce site » avec un autre message dans le journal, le guide de l’erreur critique prend le relais.