Warning: file_get_contents(): SSL operation failed. Ce message, capturé dans les logs PHP d’un site d’actualités, n’apparaissait que trois ou quatre fois par semaine, toujours sur les mêmes articles, jamais au même moment. Le point commun : chacun contenait un embed vers une plateforme vidéo externe.
Ce symptôme intermittent, difficile à reproduire à volonté, mène directement au mécanisme de cache oEmbed de WordPress et à sa durée de vie limitée. Comprendre ce mécanisme permet de transformer un temps de génération occasionnellement élevé en un comportement stable et prévisible.
Le symptôme observé
Sur la majorité des visites, l’article s’affiche normalement, en 200 à 300 millisecondes de génération. Mais de temps en temps, sans schéma évident, le temps de réponse grimpe à plusieurs secondes, parfois jusqu’au seuil de timeout PHP-FPM. Les logs serveur montrent, à ces moments précis, une requête sortante vers l’API oEmbed du fournisseur de vidéo, avec un temps de réponse variable selon la charge de ce service tiers.
Ce n’est ni un problème de base de données, ni un souci de cache de page classique : la page concernée est bien servie depuis le cache la plupart du temps. Le pic survient précisément quand le cache de page a expiré en même temps que le cache oEmbed, forçant WordPress à régénérer la page ET à réinterroger le fournisseur d’embed dans la même requête.
Le diagnostic : comment fonctionne le cache oEmbed
Quand un article contient une URL brute reconnue comme embarquable (une ligne avec juste l’URL, ou un bloc core/embed), WordPress interroge l’API oEmbed du fournisseur pour récupérer le code d’intégration, puis stocke ce résultat dans un post meta caché nommé _oembed_<hash>, accompagné d’un second meta _oembed_time_<hash> qui enregistre l’horodatage de la récupération.
Par défaut, ce cache est considéré comme valide pendant une journée. La constante utilisée en interne repose sur DAY_IN_SECONDS, appliquée dans la classe WP_Embed. Passé ce délai, la prochaine visite qui déclenche le rendu du contenu (typiquement après une invalidation de cache de page) relance l’appel HTTP sortant, de façon synchrone, dans le flux de génération de la page.

Le correctif : allonger ou fiabiliser le cache
Deux leviers permettent de limiter l’impact. Le premier consiste à allonger la durée de validité via le filtre dédié :
add_filter( 'oembed_ttl', function() {
return 30 * DAY_IN_SECONDS;
} );
Un mois de cache suffit largement pour la plupart des embeds, qui ne changent pratiquement jamais une fois publiés. Le second levier consiste à désynchroniser l’expiration du cache oEmbed de celle du cache de page, pour éviter que les deux tombent en panne au même moment et cumulent leurs délais dans une seule requête visiteur.
La prévention : basculer en tâche différée
Pour les sites à fort trafic sur des contenus riches en embeds, la solution la plus robuste consiste à préchauffer le cache oEmbed en amont, via WP-CLI, immédiatement après la publication d’un article, plutôt que d’attendre la première visite pour déclencher la résolution :
wp post list --post_type=post --fields=ID --format=csv \
| tail -n +2 \
| while read id; do
wp eval "get_post_embed_html(600, 400, get_post($id)->post_content);" --url=exemple.test
done
Cette commande force la résolution et le stockage du cache oEmbed pour chaque article, en dehors du temps de réponse subi par un visiteur réel. Combinée à un TTL allongé, cette approche élimine quasiment tout appel HTTP synchrone en contexte de production.
Une vigilance supplémentaire : les échecs de résolution
- Si l’API distante répond une erreur, WordPress met en cache un échec pendant une durée plus courte, pour éviter de marteler un service en panne.
- Un fournisseur qui change son format de réponse peut invalider silencieusement le cache existant lors de la prochaine résolution.
- Un pare-feu sortant mal configuré sur le serveur peut faire échouer la requête HTTP sans que rien ne l’indique côté WordPress, hormis un temps de génération anormal.
Notre verdict
Un embed n’est jamais gratuit : sa première résolution engage une requête HTTP sortante, et son cache, bien que fonctionnel par défaut, expire plus vite qu’on ne l’imagine. Allonger la durée de vie via oembed_ttl et préchauffer les contenus dès leur publication transforme un pic aléatoire et difficile à diagnostiquer en un coût maîtrisé, assumé une seule fois par contenu plutôt que répété au hasard des expirations.