Si votre TTFB grimpe sans raison apparente et que vos logs montrent des pages qui “attendent” la base, j’ai souvent retrouvé le même coupable : wp_options qui gonfle, un autoload trop lourd, et des transients qui s’accumulent.

Tout ce qui suit cible WordPress 6.9.4 (avril 2026) et PHP 8.1+. L’objectif n’est pas de “faire le ménage” au hasard, mais de mesurer, cibler, et automatiser sans casser le site.


Le problème de performance

Quand wp_options devient trop volumineuse (ou mal utilisée), WordPress charge plus de données que nécessaire à chaque requête. Le cas classique : un autoload qui passe à plusieurs mégaoctets, ce qui alourdit le bootstrap et augmente le TTFB.

En coulisses, WordPress charge les options marquées autoload = yes très tôt. Sur un site “normal”, ça va. Sur un site avec Elementor, Divi 5, Avada, 30 plugins marketing, des A/B tests et des snippets, j’ai déjà vu 8–15 Mo d’options autoloadées. Résultat : pages lentes, CPU MySQL qui monte, et Core Web Vitals qui se dégradent (notamment LCP si le serveur met trop de temps à répondre).

Impact concret :

  • SEO : TTFB élevé, crawl moins efficace, signaux de performance dégradés.
  • Taux de rebond : sur mobile, une seconde de plus se ressent immédiatement.
  • Stabilité : des requêtes plus lourdes augmentent la probabilité de timeouts (PHP-FPM, proxy, CDN).

À la fin, vous saurez :

  • mesurer le poids réel de autoload et identifier les options problématiques,
  • nettoyer transients et options orphelines de façon réversible,
  • corriger votre code (ou vos snippets) qui génèrent trop d’options/transients,
  • valider l’amélioration avec des mesures reproductibles.

Résumé rapide

  • Mesurez d’abord : taille autoload, top options, requêtes lentes (Query Monitor + slow query log).
  • Visez un autoload raisonnable (souvent < 1–2 Mo ; au-delà de 3–5 Mo, ça devient fréquemment visible).
  • Nettoyez les transients expirés (et comprenez pourquoi ils restent).
  • Traitez les options orphelines avec une liste blanche/noire, pas au jugé.
  • Corrigez le code : évitez de stocker des blobs énormes dans wp_options, utilisez l’object cache et des expirations cohérentes.
  • Automatisez un nettoyage safe via WP-CLI + sauvegarde + dry-run.

Diagnostic avec du code

1) Mesurer le poids de l’autoload (WP-CLI)

Commencez par une mesure brute. Sur un serveur, WP-CLI est souvent le moyen le plus fiable (et plus rapide que phpMyAdmin).

# Taille totale autoload (approx) : somme des longueurs des option_value
wp db query "
SELECT
  ROUND(SUM(LENGTH(option_value))/1024/1024, 2) AS autoload_mb,
  COUNT(*) AS autoload_count
FROM wp_options
WHERE autoload IN ('yes','on','auto');
"

Ensuite, listez les plus gros contributeurs :

wp db query "
SELECT
  option_name,
  autoload,
  ROUND(LENGTH(option_value)/1024, 1) AS size_kb
FROM wp_options
WHERE autoload IN ('yes','on','auto')
ORDER BY LENGTH(option_value) DESC
LIMIT 50;
"

Note : certains hébergeurs utilisent on ou auto selon des migrations/outils. Sur WordPress, la valeur attendue est yes/no, mais je préfère être large en diagnostic.

2) Repérer les requêtes lentes (Query Monitor + logs)

Query Monitor reste un standard pour comprendre ce qui se passe sur une page précise. Installez-le et activez-le temporairement sur un environnement de staging si possible.

Plugin officiel : Query Monitor.

Côté serveur, activez le slow query log MySQL/MariaDB si vous avez la main. Exemple MySQL (à adapter à votre infra) :

# Exemple (à faire côté serveur, nécessite droits admin)
# slow_query_log=1
# long_query_time=0.2
# slow_query_log_file=/var/log/mysql/slow.log

Vous cherchez typiquement :

  • des SELECT option_name, option_value FROM wp_options WHERE autoload = 'yes' qui prennent trop de temps (souvent signe de table gonflée + stockage/CPU),
  • des séries de get_option() sur des options non autoloadées (N+1 options).

3) Activer des logs WordPress utiles (wp-config.php)

Sur WordPress 6.9.4, rien de magique : vous voulez des logs propres, sans affichage en front.

/* wp-config.php */

/* Journalisation utile en staging / hors heures de pointe */
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);

/* Limite les écritures inutiles (révisions) si votre site est très éditorial */
define('WP_POST_REVISIONS', 20);

Risque : activer WP_DEBUG sur production peut exposer des chemins ou des notices si un plugin affiche les erreurs. Gardez WP_DEBUG_DISPLAY à false et surveillez le fichier wp-content/debug.log.

4) Mini-profiling maison : mesurer le bootstrap et l’impact options

J’utilise souvent un MU-plugin de diagnostic que je déploie 10 minutes, puis je supprime. Il logge le temps total, la mémoire, et il donne une estimation du poids autoload en mémoire.

<?php
/**
 * Plugin Name: MU - Perf Options Probe
 * Description: Sondes temporaires pour diagnostiquer autoload/wp_options (WordPress 6.9.4+).
 * Author: Votre équipe
 */

if (!defined('ABSPATH')) {
	exit;
}

/* ⚠️ À placer dans wp-content/mu-plugins/perf-options-probe.php */
add_action('muplugins_loaded', function () {
	if (!defined('WP_DEBUG_LOG') || !WP_DEBUG_LOG) {
		return;
	}
	$GLOBALS['bpcab_perf_t0'] = hrtime(true);
	$GLOBALS['bpcab_perf_m0'] = memory_get_usage(true);
}, 1);

add_action('shutdown', function () {
	if (!defined('WP_DEBUG_LOG') || !WP_DEBUG_LOG) {
		return;
	}

	$t0 = $GLOBALS['bpcab_perf_t0'] ?? null;
	$m0 = $GLOBALS['bpcab_perf_m0'] ?? null;

	$ms = $t0 ? (hrtime(true) - $t0) / 1e6 : null;
	$mem_delta = ($m0 !== null) ? (memory_get_usage(true) - $m0) : null;

	/* Estimation : options autoloadées dans le cache interne 'alloptions' */
	$alloptions = wp_load_alloptions(); // Cache + requête si nécessaire
	$autoload_bytes = strlen(maybe_serialize($alloptions));

	error_log(sprintf(
		'[perf] uri=%s time_ms=%.1f mem_delta_mb=%.2f autoload_est_mb=%.2f',
		$_SERVER['REQUEST_URI'] ?? '',
		$ms ?? -1,
		$mem_delta ? ($mem_delta / 1024 / 1024) : -1,
		$autoload_bytes / 1024 / 1024
	));
}, 9999);

Ce log n’est pas “scientifique”, mais il est excellent pour comparer avant/après sur les mêmes URLs, surtout si vous videz les caches entre les tests.

Étape 1 : Auditer et réduire autoload dans wp_options

Pourquoi autoload coûte cher

À chaque requête, WordPress charge les options autoloadées très tôt pour éviter des requêtes multiples. Si votre autoload contient des gros tableaux sérialisés (constructeurs de pages, options de thème, caches maison), vous payez ce coût sur chaque page, même quand la page n’en a pas besoin.

Le piège que je vois le plus : des plugins stockent des “caches” applicatifs dans wp_options avec autoload activé par défaut. Ça marche… jusqu’à ce que ça ne marche plus.

Code AVANT (lent) : stocker un gros cache en autoload

Exemple typique (snippet de plugin) : on enregistre un gros tableau de données en option, sans désactiver autoload.

// ❌ Mauvais : cache volumineux en option autoloadée
$big_cache = [
	/* ... gros tableau ... */
];
update_option('myplugin_big_cache', $big_cache);

Code APRÈS (optimisé) : option non-autoload + expiration via transient

Pour un cache, préférez un transient (et idéalement un object cache persistant). Si vous devez utiliser une option, désactivez l’autoload.

// ✅ Mieux : transient avec expiration
set_transient('myplugin_big_cache', $big_cache, 15 * MINUTE_IN_SECONDS);

// ✅ Si option nécessaire : désactiver autoload explicitement
update_option('myplugin_big_cache', $big_cache, false); // false = autoload désactivé

Pourquoi c’est plus rapide : l’option n’est plus chargée systématiquement. Le transient est lu seulement si vous en avez besoin, et il peut être servi par Redis/Memcached si configuré.

Audit ciblé : basculer des options massives hors autoload (avec prudence)

Ne faites pas un UPDATE en masse “à l’aveugle”. Je préfère une approche :

  • identifier les 20–50 plus grosses options autoloadées,
  • repérer celles qui sont clairement des caches, logs, listes d’IDs, ou données “rarement nécessaires”,
  • désactiver autoload une par une, en mesurant.

WP-CLI : basculer une option en no-autoload

WordPress ne fournit pas une commande WP-CLI “native” pour modifier autoload proprement. Le plus simple reste une requête SQL, mais faites-le avec sauvegarde et sur staging d’abord.

# Sauvegarde rapide (au minimum)
wp db export ~/backup-before-autoload.sql

# Exemple : désactiver autoload pour une option spécifique
wp db query "
UPDATE wp_options
SET autoload = 'no'
WHERE option_name = 'myplugin_big_cache';
"

Mesure avant/après (même commande qu’au diagnostic)

wp db query "
SELECT ROUND(SUM(LENGTH(option_value))/1024/1024, 2) AS autoload_mb
FROM wp_options
WHERE autoload IN ('yes','on','auto');
"

Mesure d’impact : TTFB et temps PHP

Sur un site réel, après avoir réduit autoload de quelques Mo, j’observe souvent :

  • un gain TTFB de 50 à 300 ms (selon CPU/DB/IO),
  • une baisse de la mémoire PHP,
  • moins de variance (p95 plus stable).

Pour mesurer côté client sans outils graphiques, utilisez curl et regardez le “time_starttransfer”.

curl -o /dev/null -s -w "ttfb=%{time_starttransfer}s total=%{time_total}sn" https://example.com/

Étape 2 : Nettoyer les transients et leur expiration

Pourquoi des transients expirés restent dans la base

Les transients stockés en base reposent sur une expiration “logique”. Beaucoup ne sont supprimés qu’à l’occasion d’accès (lazy deletion) ou via des routines. Si votre site n’accède jamais à certains transients, ils peuvent rester.

Autre cas fréquent : un object cache persistant (Redis) est activé, puis désactivé. Les transients changent de backend, et vous vous retrouvez avec des restes en base.

Diagnostic : compter les transients en base

wp db query "
SELECT
  SUM(option_name LIKE '_transient_%') AS transient_rows,
  SUM(option_name LIKE '_transient_timeout_%') AS transient_timeout_rows
FROM wp_options;
"

Nettoyage : supprimer uniquement les expirés (SQL)

Voici un nettoyage qui vise les expirés, en se basant sur les lignes _transient_timeout_*. Testez d’abord en “dry run” (SELECT), puis DELETE.

Dry run (liste)

wp db query "
SELECT
  REPLACE(option_name, '_transient_timeout_', '') AS transient_key,
  option_value AS expires_at
FROM wp_options
WHERE option_name LIKE '_transient_timeout_%'
  AND option_value < UNIX_TIMESTAMP()
LIMIT 200;
"

Suppression (expirés seulement)

wp db query "
DELETE o, t
FROM wp_options o
JOIN wp_options t
  ON t.option_name = CONCAT('_transient_timeout_', SUBSTRING(o.option_name, 12))
WHERE o.option_name LIKE '_transient_%'
  AND o.option_name NOT LIKE '_transient_timeout_%'
  AND t.option_name LIKE '_transient_timeout_%'
  AND t.option_value < UNIX_TIMESTAMP();
"

Ce DELETE supprime à la fois la valeur et son timeout. Sur de grosses tables, il peut verrouiller : faites-le en heures creuses et, si nécessaire, en batches (LIMIT via sous-requêtes).

Alternative safe côté PHP (batch) : cron interne

Quand je n’ai pas la main sur SQL (hébergement mutualisé), je passe par un batch PHP. C’est plus lent, mais contrôlable et journalisable.

<?php
/**
 * Plugin Name: Cleanup - Expired Transients (Batch)
 * Description: Nettoyage batch des transients expirés en base (WordPress 6.9.4+).
 */

if (!defined('ABSPATH')) exit;

add_action('init', function () {
	// ⚠️ Évitez de lancer ça sur chaque hit : utilisez WP-Cron ou WP-CLI.
});

add_action('bpcab_cleanup_expired_transients', function () {
	global $wpdb;

	$limit = 500; // Batch raisonnable
	$now = time();

	// On récupère les timeouts expirés
	$timeout_like = $wpdb->esc_like('_transient_timeout_') . '%';

	$timeout_rows = $wpdb->get_results(
		$wpdb->prepare(
			"SELECT option_name
			 FROM {$wpdb->options}
			 WHERE option_name LIKE %s
			   AND option_value < %d
			 LIMIT %d",
			$timeout_like,
			$now,
			$limit
		)
	);

	if (empty($timeout_rows)) {
		return;
	}

	$keys = [];
	foreach ($timeout_rows as $row) {
		$keys[] = str_replace('_transient_timeout_', '', $row->option_name);
	}

	// Suppression des valeurs + timeouts
	foreach ($keys as $key) {
		delete_transient($key);
	}

	error_log(sprintf('[cleanup] deleted_expired_transients=%d', count($keys)));
});

/* Planification quotidienne (à activer une fois) */
register_activation_hook(__FILE__, function () {
	if (!wp_next_scheduled('bpcab_cleanup_expired_transients')) {
		wp_schedule_event(time() + 300, 'daily', 'bpcab_cleanup_expired_transients');
	}
});

register_deactivation_hook(__FILE__, function () {
	wp_clear_scheduled_hook('bpcab_cleanup_expired_transients');
});

Piège : si vous copiez ce code dans un plugin de snippets qui ne supporte pas register_activation_hook(), l’event ne sera jamais planifié. Dans ce cas, déclenchez-le via WP-CLI ou ajoutez une page d’admin dédiée.

Étape 3 : Trier wp_options orphelines et plugins “fantômes”

Ce que j’appelle “fantôme”

Vous avez désinstallé un plugin, mais ses options restent. C’est normal : beaucoup de plugins ne nettoient pas tout (par prudence). Sur des années, ça transforme wp_options en grenier.

Le risque : supprimer une option encore utilisée (par un mu-plugin, un thème, un builder). Avec Divi 5, Elementor ou Avada, certaines options peuvent être partagées entre modules. Ne supprimez pas “par motif” uniquement.

Audit : retrouver les préfixes dominants

wp db query "
SELECT
  SUBSTRING_INDEX(option_name, '_', 1) AS prefix_guess,
  COUNT(*) AS cnt
FROM wp_options
GROUP BY prefix_guess
ORDER BY cnt DESC
LIMIT 50;
"

Ce n’est pas parfait (les options n’ont pas toutes un préfixe), mais ça donne une carte rapide.

Approche safe : exporter une liste candidate, valider, puis supprimer

Je procède en 3 temps :

  1. exporter une liste d’options candidates (par préfixe),
  2. chercher dans le code (dossier plugins/thèmes) si ces clés existent,
  3. supprimer en batch avec sauvegarde.

1) Exporter (CSV)

wp db query --skip-column-names "
SELECT option_name
FROM wp_options
WHERE option_name LIKE 'oldplugin_%'
ORDER BY option_name;
" > /tmp/oldplugin-options.txt

2) Vérifier si le code les référence

# Sur le serveur (ou via un checkout git), cherchez des get_option/update_option
grep -R "oldplugin_" -n wp-content/plugins wp-content/themes 2>/dev/null | head -n 50

3) Supprimer (après sauvegarde)

wp db export ~/backup-before-options-clean.sql

wp db query "
DELETE FROM wp_options
WHERE option_name LIKE 'oldplugin_%';
"

Cas particulier : options énormes sérialisées

Quand une option est énorme (plusieurs centaines de Ko), regardez ce qu’elle contient avant de décider.

wp option get my_huge_option --format=json | head -c 2000

Piège : wp option get peut exploser votre terminal si l’option est massive. Utilisez --format=json et tronquez.

Étape 4 : Optimiser vos lectures d’options côté code

Anti-pattern : get_option() en boucle (N+1 options)

J’ai souvent vu ça dans des widgets Elementor custom, des modules Divi 5 maison, ou des shortcodes Avada : chaque rendu appelle 10–50 get_option() différents. Même si l’object cache interne limite, vous créez du CPU PHP, et parfois des queries si l’option n’est pas en cache.

// ❌ Mauvais : 30 appels get_option() dans un rendu
$title = get_option('my_title');
$subtitle = get_option('my_subtitle');
$color = get_option('my_color');
// ... etc

Optimisation : regrouper dans une seule option (petite) ou utiliser une option autoloadée (contrôlée)

Le regroupement n’est pas toujours une bonne idée si ça grossit trop. Mais pour 10–30 valeurs simples, une option “settings” compacte est souvent plus efficace.

// ✅ Mieux : une option compacte
$settings = get_option('myplugin_settings', []);
$title = $settings['title'] ?? '';
$subtitle = $settings['subtitle'] ?? '';
$color = $settings['color'] ?? '#000';

Si cette option est lue sur toutes les pages, vous pouvez la laisser autoloadée si elle reste petite (quelques Ko). Le but n’est pas “autoload = mal”, c’est “autoload = doit rester léger et utile globalement”.

Builders (Divi 5 / Elementor / Avada) : éviter de recalculer des options à chaque module

Sur une page construite avec un builder, vous pouvez avoir des dizaines de modules. Un pattern simple : mettre en cache (runtime) vos settings dans une variable statique.

function myplugin_get_settings_cached(): array {
	static $cache = null;

	if ($cache !== null) {
		return $cache;
	}

	// ✅ Un seul get_option par requête
	$cache = get_option('myplugin_settings', []);
	return $cache;
}

Ça ne remplace pas Redis, mais ça évite de refaire du travail PHP dans la même requête (notamment si votre code normalise/valide les settings).

Étape 5 : Object cache pour éviter de refaire les mêmes requêtes

Pourquoi ça change le nettoyage wp_options

Avec un object cache persistant (Redis/Memcached), beaucoup de lectures d’options/transients ne touchent plus MySQL. C’est souvent le plus gros levier “perçu” sur mobile (TTFB). Mais attention : si vous laissez autoload exploser, vous allez surtout déplacer le problème (mémoire Redis + sérialisation).

Code : utiliser wp_cache_* pour vos données calculées (pas wp_options)

Pour des données calculées (ex: liste d’IDs, mapping), utilisez l’object cache avec un groupe dédié.

function myplugin_get_expensive_map(): array {
	$cache_key = 'expensive_map_v1';
	$group = 'myplugin';

	$cached = wp_cache_get($cache_key, $group);
	if (is_array($cached)) {
		return $cached;
	}

	// ⚠️ Calcul coûteux (ex: requêtes, agrégations)
	$map = [
		// ...
	];

	// Cache 10 minutes (si object cache persistant, c’est très efficace)
	wp_cache_set($cache_key, $map, $group, 10 * MINUTE_IN_SECONDS);

	return $map;
}

Si vous utilisez un plugin Redis, vérifiez qu’il respecte bien l’expiration. Certains setups “drop-in” ont des comportements différents selon versions. Testez.

Configuration serveur

.htaccess (Apache) : compression + cache navigateur (assets)

Ce n’est pas spécifique à wp_options, mais si vous corrigez le TTFB sans corriger le reste, vous verrez encore des scores moyens. Voici un bloc standard, compatible avec la plupart des hébergements Apache.

# .htaccess (Apache) - à placer dans le vhost ou à la racine WordPress si autorisé

<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/html text/plain text/css application/javascript application/json image/svg+xml
</IfModule>

<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresByType text/css "access plus 7 days"
  ExpiresByType application/javascript "access plus 7 days"
  ExpiresByType image/svg+xml "access plus 30 days"
  ExpiresByType image/png "access plus 30 days"
  ExpiresByType image/jpeg "access plus 30 days"
</IfModule>

php.ini / PHP-FPM : OPcache (indirect mais réel)

Si votre serveur n’a pas OPcache correctement configuré, vous aurez un TTFB instable, et vous risquez d’attribuer à tort le problème à MySQL.

; php.ini (exemples)
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=40000
opcache.validate_timestamps=1
opcache.revalidate_freq=60

Référence : PHP OPcache (php.net).

wp-config.php : désactiver les “autosaves” excessives côté DB (selon usage)

Ce n’est pas wp_options, mais sur des sites éditoriaux, réduire le bruit DB aide la latence globale.

/* wp-config.php */
define('AUTOSAVE_INTERVAL', 120); // 2 minutes (au lieu de 60s)

Vérification des résultats

1) Mesures DB reproductibles

Avant/après, capturez :

  • taille autoload (Mo),
  • top 20 options autoloadées (et leur taille),
  • nombre de transients expirés supprimés.
# Autoload total
wp db query "
SELECT ROUND(SUM(LENGTH(option_value))/1024/1024, 2) AS autoload_mb
FROM wp_options
WHERE autoload IN ('yes','on','auto');
"

# Top 20 autoload
wp db query "
SELECT option_name, ROUND(LENGTH(option_value)/1024, 1) AS size_kb
FROM wp_options
WHERE autoload IN ('yes','on','auto')
ORDER BY LENGTH(option_value) DESC
LIMIT 20;
"

2) Mesures HTTP (TTFB) sur plusieurs runs

Faites 10 runs et regardez la médiane.

for i in $(seq 1 10); do
  curl -o /dev/null -s -w "run=$i ttfb=%{time_starttransfer} total=%{time_total}n" https://example.com/
done

3) Vérifier que vous n’avez rien cassé

Après avoir désactivé autoload sur une option, le risque est rarement un fatal error. Le risque réel : des réglages “disparaissent” dans l’admin, un module de builder qui perd une config, ou un plugin qui recalcule un cache en boucle.

  • Testez l’admin (pages de réglages des plugins concernés).
  • Testez une page builder lourde (Elementor/Divi/Avada) + une page article simple.
  • Surveillez debug.log pendant 30 minutes.

Si les performances ne s’améliorent pas

Étape A : vérifier que vous mesurez sans cache de page

Erreur très fréquente : vous comparez “avant” sans cache de page et “après” avec cache (ou l’inverse). Désactivez temporairement le cache de page, ou ajoutez un header/paramètre de bypass si votre stack le permet.

Si vous êtes derrière un CDN, purge complète obligatoire. J’ai déjà vu des gens optimiser wp_options puis “ne rien voir” parce que Cloudflare servait l’ancienne version en cache.

Étape B : inspecter les requêtes lentes hors wp_options

Si autoload est passé de 6 Mo à 1 Mo et que le TTFB ne bouge pas, cherchez :

  • requêtes wp_postmeta non indexées (plugins de filtres, WooCommerce),
  • requêtes sur wp_terms/wp_term_relationships (builders + taxonomies),
  • HTTP calls externes (API marketing) bloquants.

Étape C : vérifier la santé de la table options

Sur MySQL, une table peut être fragmentée. Selon l’engine et la charge, un OPTIMIZE TABLE peut aider (ou pas). Faites-le en maintenance, car ça peut verrouiller.

wp db query "OPTIMIZE TABLE wp_options;"

Étape D : vérifier que PHP 8.1+ est réellement actif

Je vois encore des serveurs “annoncés” en 8.1 mais avec un cron en 7.4, ou un FPM pool différent. Vérifiez via WP-CLI :

php -v
wp --info | sed -n '1,25p'

Pièges et erreurs courantes

Symptôme Cause probable Vérification Solution
TTFB élevé, autoload > 5 Mo Options “cache” stockées en autoload Top 50 options autoload (SQL) Désactiver autoload sur options non critiques, migrer caches vers transients/object cache
Transients expirés en masse Pas d’accès déclenchant la suppression + pas de routine de cleanup SELECT sur _transient_timeout_% expirés Batch SQL en heures creuses ou cron PHP/CLI
Vous cassez un réglage plugin Suppression trop agressive dans wp_options Comparer backup / rechercher la clé dans le code Restaurer, puis supprimer par petites itérations avec liste candidate
Aucun gain après nettoyage Cache de page/CDN fausse la mesure ou goulot ailleurs curl (ttfb) avec bypass cache + Query Monitor Mesurer sans cache, puis profiler requêtes lourdes (postmeta/terms/API externes)
Erreur après ajout du code Snippet collé au mauvais endroit, point-virgule manquant, hook inadapté debug.log + désactivation du snippet Mettre le code en MU-plugin, vérifier syntaxe, choisir le bon hook + priorité
Nettoyage “cron” ne tourne jamais Event non planifié (snippet plugin), WP-Cron désactivé wp cron event list Planifier via WP-CLI, ou cron système, ou plugin complet

Erreurs réalistes que je vois passer

  • Tester directement en production : faites au minimum un export DB avant chaque batch de suppression.
  • Copier un vieux snippet qui manipule autoload avec des hypothèses fausses (valeurs yes/no uniquement, pas de variantes). Sur WordPress 6.9.4, gardez votre diagnostic ouvert, mais normalisez ensuite.
  • Oublier de purger les caches (plugin cache, CDN, cache navigateur) et conclure “ça ne marche pas”.
  • Supprimer des options de builder parce que “ça ressemble à un cache”. Divi/Elementor/Avada stockent parfois des structures nécessaires au rendu.

Conseils de maintenance

1) Mettre un garde-fou : alerte si autoload dépasse un seuil

Un petit check hebdomadaire via WP-CLI + cron système, et vous évitez de redécouvrir le problème dans 6 mois.

# Exemple de check (à mettre dans un cron)
AUTOLOAD_MB=$(wp db query --skip-column-names "
SELECT ROUND(SUM(LENGTH(option_value))/1024/1024, 2)
FROM wp_options
WHERE autoload IN ('yes','on','auto');
")
echo "autoload_mb=$AUTOLOAD_MB"

2) Forcer les plugins maison à ne pas polluer autoload

Règle simple que j’impose : toute option > 20–50 Ko doit être non-autoload par défaut, sauf justification.

function myplugin_update_option_no_autoload(string $name, $value): void {
	// ✅ Pattern : forcer no-autoload pour éviter les dérives
	update_option($name, $value, false);
}

3) Nettoyage transients : mensuel, pas quotidien

Un cleanup trop fréquent peut faire l’inverse : plus de charge DB. Mensuel suffit dans la plupart des cas, sauf site très dynamique.

4) Sur sites builders : éviter les “caches” en options

Pour des modules Elementor/Divi/Avada, préférez :

  • cache runtime (variable statique),
  • object cache (wp_cache_set),
  • transients avec expiration courte.

Ressources

FAQ

Quelle taille d’autoload est “normale” ?

Ça dépend du stack, mais au-delà de 1–2 Mo, je commence à regarder. À partir de 3–5 Mo, c’est souvent corrélé à un TTFB plus élevé, surtout sur des serveurs modestes. J’ai déjà vu des sites à 10+ Mo : là, le gain après réduction est généralement évident.

Est-ce dangereux de passer une option de autoload=yes à no ?

En général, non : l’option reste accessible via get_option(). Le risque est plutôt un plugin qui s’attend à trouver ses settings déjà chargés (mauvaise pratique) et qui se met à recalculer en boucle. Faites-le option par option, et surveillez les logs.

Puis-je supprimer tous les transients ?

Techniquement oui, mais vous risquez un “cache stampede” (plein de recalculs d’un coup). Préférez supprimer les expirés, puis, si nécessaire, purger les non-expirés par lots en heures creuses.

Pourquoi ai-je des transients expirés alors que j’ai Redis ?

Parce que tous les transients ne sont pas forcément dans Redis (selon config), ou parce que Redis a été activé après coup, laissant des restes en base. Vérifiez aussi que le drop-in object cache est bien actif.

Est-ce que Query Monitor suffit pour diagnostiquer wp_options ?

Il aide beaucoup, mais il ne remplace pas une vue globale (taille autoload, top options). Je combine : Query Monitor pour la page, WP-CLI/SQL pour la table.

Comment savoir si une option est “orpheline” ?

Il n’y a pas de vérité parfaite. Je pars d’un préfixe, je cherche dans le code (grep), je vérifie si le plugin est encore installé, puis je supprime en batch. Sans sauvegarde, c’est une mauvaise idée.

Elementor/Divi/Avada stockent-ils beaucoup dans wp_options ?

Oui, et c’est normal. Le problème n’est pas qu’ils stockent, c’est quand des données volumineuses deviennent autoloadées alors qu’elles ne sont pas nécessaires sur toutes les pages. Sur des sites builders, la discipline sur autoload est encore plus rentable.

Après nettoyage, mon score CWV ne bouge pas. Pourquoi ?

Parce que CWV dépend aussi du front : JS, CSS, images, rendu. Le nettoyage wp_options agit surtout sur le TTFB et la stabilité serveur. Mesurez d’abord time_starttransfer et la charge DB, puis attaquez les assets.

Dois-je faire un OPTIMIZE TABLE sur wp_options ?

Parfois utile, parfois neutre, parfois coûteux. Faites-le en maintenance, après un gros nettoyage, et vérifiez l’impact via slow query log et TTFB.

Quel est le meilleur ordre d’action ?

1) mesurer autoload + top options, 2) réduire autoload des gros “caches”, 3) nettoyer transients expirés, 4) traiter options orphelines, 5) ajouter object cache si pertinent, 6) valider sur plusieurs URLs.