Le WordPress d'aujourd'hui, décodé pour les développeurs

Erreurs WordPress · Serveur et codes HTTP

cURL error 28: Operation timed out sur WordPress : solutions

HTTP / cURL

« cURL error 28: Operation timed out » dans la Santé du site ou les mises à jour WordPress : testez la requête sortante, DNS, pare-feu, boucle locale et délai.

Message affiché

cURL error 28: Operation timed out after 5001 milliseconds with 0 bytes received

En anglais : cURL error 28: Operation timed out after 5001 milliseconds with 0 bytes received

Réponse rapide

Une requête sortante de WordPress (mises à jour, API, requête de bouclage) n’a pas obtenu de réponse dans le délai de 5 secondes. Testez la requête avec curl depuis le serveur : pare-feu sortant, DNS ou IPv6 lent, service distant ou boucle bloquée.

Dans l’écran « Santé du site », sous « Mises à jour » ou dans le journal d’erreurs, vous lisez « cURL error 28: Operation timed out after 5001 milliseconds with 0 bytes received ». Selon l’endroit, il s’accompagne d’un test critique (« Votre site n’a pas pu terminer la requête de bouclage »), d’un message « Impossible de rejoindre WordPress.org », de mises à jour qui ne se détectent plus, de tâches planifiées qui ne se lancent pas, ou d’une extension qui n’arrive pas à joindre son service distant.

Il s’agit d’une erreur de requête sortante : c’est votre serveur WordPress, et non le visiteur, qui a tenté d’appeler une autre adresse (le site de WordPress, une API, ou lui-même) sans obtenir de réponse à temps. Le visiteur ne voit généralement rien ; c’est l’administrateur qui le constate.

Ce que signifie cette erreur

WordPress envoie ses requêtes HTTP avec la classe WP_Http (wp-includes/class-wp-http.php), qui s’appuie sur la bibliothèque Requests et, quand l’extension PHP cURL est disponible, sur le transport cURL (wp-includes/Requests/src/Transport/Curl.php). Si cURL échoue, Requests construit le message « cURL error %s: %s » à partir du code d’erreur et du texte de libcurl, et WordPress le renvoie dans un objet WP_Error de code http_request_failed.

Le code 28 de cURL correspond à CURLE_OPERATION_TIMEDOUT : le délai est écoulé. Le texte après les deux-points précise à quel stade, avec des variantes à connaître :

  • Resolving timed out after N milliseconds : la résolution DNS n’a pas abouti à temps ;
  • Connection timed out after N milliseconds : la connexion TCP n’a pas pu être établie (pare-feu qui ignore la requête, adresse injoignable) ;
  • Operation timed out after N milliseconds with 0 bytes received : la connexion est établie, mais aucune réponse n’est arrivée (service distant lent, serveur local saturé) ;
  • Operation timed out after N milliseconds with X out of Y bytes received : la réponse a commencé mais n’a pas été terminée.

Le délai par défaut est court : 5 secondes (wp-includes/class-wp-http.php, filtre http_request_timeout). Un service un peu lent suffit donc à déclencher l’erreur. Elle apparaît à plusieurs endroits du cœur : le test de bouclage de la Santé du site (wp-admin/includes/class-wp-site-health.php), le test de communication avec WordPress.org, les contrôles de mises à jour (wp-includes/update.php), et le déclenchement des tâches planifiées. Pour un délai dépassé côté navigateur, voyez plutôt la fiche erreur 504.

Diagnostic rapide

Symptôme / constatCause probableÀ vérifier
Seul le test de requête de bouclage échoueLe serveur ne parvient pas à se joindre lui-même (DNS, pare-feu, authentification, processus saturés)curl -I https://votre-site/ depuis le serveur ; fichier hosts
Impossible de rejoindre WordPress.org, mises à jour introuvablesPare-feu sortant, DNS, IPv6, HTTP bloqué par WP_HTTP_BLOCK_EXTERNALcurl -v https://api.wordpress.org/ ; test « Requêtes HTTP » de la Santé du site
« Resolving timed out »Résolveur DNS lent ou injoignable/etc/resolv.conf ; dig api.wordpress.org
« Connection timed out »Pare-feu qui ignore les paquets, IPv6 mal routécurl -4 contre curl -6 ; règles sortantes
« Operation timed out… 0 bytes received »Service distant lent ou serveur local saturéStatut du service ; processus PHP-FPM disponibles
Une seule extension concernéeSa requête vers un service précis est trop lenteJournal de l’extension ; délai fixé dans son code

Les causes les plus fréquentes

  1. La requête de bouclage vers le site lui-même bloquée : mot de passe HTTP (authentification basique), pare-feu, extension de maintenance, domaine qui pointe vers le CDN que le serveur ne peut pas joindre.
  2. Un pare-feu sortant ou une règle de l’hébergeur qui empêche le serveur d’appeler l’extérieur.
  3. Un problème de DNS ou d’IPv6 : le résolveur répond lentement, ou le serveur essaie l’IPv6 puis attend le délai avant de basculer sur l’IPv4.
  4. Un service distant lent ou en panne : API de licence, d’e-mailing, de paiement, serveur de mises à jour.
  5. Un serveur local saturé : si tous les processus PHP sont occupés, la requête de bouclage attend un processus libre et expire.
  6. Un délai de 5 secondes trop court pour l’opération (téléchargement, appel d’API volumineux).

Solutions pas à pas

Ces opérations sont en lecture seule, sauf mention contraire. Du moins invasif au plus technique :

1. Tester la requête depuis le serveur lui-même

Reproduisez l’appel en ligne de commande, depuis le serveur qui héberge WordPress (pas depuis votre poste) :

# Vers WordPress.org
curl -sS -m 10 -o /dev/null -w '%{http_code} en %{time_total} s\n' https://api.wordpress.org/core/version-check/1.7/

# Vers votre propre site (requête de bouclage)
curl -sS -m 10 -o /dev/null -w '%{http_code} en %{time_total} s\n' https://www.exemple.fr/wp-cron.php

# Forcer IPv4 ou IPv6 pour isoler un problème de protocole
curl -4 -sS -m 10 -o /dev/null -w '%{http_code}\n' https://api.wordpress.org/
curl -6 -sS -m 10 -o /dev/null -w '%{http_code}\n' https://api.wordpress.org/

Un échec identique à celui de WordPress confirme que le problème est réseau ou serveur, pas WordPress. Si -4 réussit et -6 expire, la cause est l’IPv6. Pour tester depuis WordPress même, avec WP-CLI :

wp eval '$r = wp_remote_get( "https://api.wordpress.org/core/version-check/1.7/" ); echo is_wp_error( $r ) ? $r->get_error_message() : wp_remote_retrieve_response_code( $r );'

2. Réparer la requête de bouclage

Un serveur qui n’arrive pas à joindre son propre domaine est le cas le plus fréquent. Si le domaine passe par un CDN ou un proxy que le serveur ne peut pas atteindre, faites-le pointer vers lui-même dans le fichier hosts :

# /etc/hosts
127.0.0.1   www.exemple.fr exemple.fr

Si votre site est protégé par un mot de passe HTTP, WordPress ajoute ses identifiants à la requête de bouclage, mais une extension de sécurité ou un pare-feu peut la refuser : vérifiez dans l’interface de l’extension que l’adresse IP du serveur est autorisée. Une extension de maintenance qui répond 503 au bouclage fausse aussi le test.

3. Corriger le DNS et l’IPv6

Un résolveur lent est visible avec dig api.wordpress.org (cherchez la ligne Query time). Utilisez un résolveur fiable dans /etc/resolv.conf ou la configuration réseau de votre distribution. Si l’IPv6 est en cause et que vous ne l’utilisez pas, faites préférer l’IPv4 dans la résolution d’adresses du système :

# /etc/gai.conf : préférer l'IPv4
precedence ::ffff:0:0/96  100

Cette modification touche tout le système : sur un mutualisé, demandez à l’hébergeur de corriger le routage IPv6 sortant.

4. Vérifier le pare-feu sortant et le blocage de WordPress

Un pare-feu qui filtre le trafic sortant doit autoriser le port 443 vers api.wordpress.org, downloads.wordpress.org et les services que vos extensions appellent. Vérifiez aussi que votre wp-config.php ne bloque pas les appels externes :

wp config get WP_HTTP_BLOCK_EXTERNAL
wp config get WP_ACCESSIBLE_HOSTS

Si WP_HTTP_BLOCK_EXTERNAL est vrai, seuls les hôtes de WP_ACCESSIBLE_HOSTS sont joignables, et la Santé du site l’indique par « Les requêtes HTTP sont bloquées ».

5. Libérer le serveur local

Si les processus PHP sont tous occupés, la requête de bouclage attend et expire. Dans le journal de PHP-FPM, cherchez « server reached pm.max_children setting » et, si besoin, relevez le nombre de processus comme dans la fiche erreur 503. Les tâches planifiées s’exécutent aussi par ce bouclage : mieux vaut les confier à un vrai cron, comme l’expliquent wp-cron : comprendre et maîtriser la planification et les pièges classiques de WP-Cron.

// wp-config.php
define( 'DISABLE_WP_CRON', true );

# crontab du serveur (toutes les 5 minutes)
*/5 * * * * cd /var/www/exemple/html && wp cron event run --due-now >/dev/null 2>&1

6. Allonger le délai pour une requête précise

Si le service distant est simplement lent, donnez plus de temps à l’appel concerné. Dans votre propre code, passez un délai explicite :

$reponse = wp_remote_get( $url, array( 'timeout' => 20 ) );
if ( is_wp_error( $reponse ) ) {
    error_log( 'Appel API : ' . $reponse->get_error_message() );
}

Pour une extension dont vous ne contrôlez pas le code, allongez le délai pour un hôte donné avec le filtre http_request_args, sans toucher aux autres requêtes :

add_filter( 'http_request_args', function ( $args, $url ) {
    if ( 'api.exemple.com' === wp_parse_url( $url, PHP_URL_HOST ) ) {
        $args['timeout'] = 30;
    }
    return $args;
}, 10, 2 );

Le cas d’une API qui se met à expirer en production est décrit dans une API REST WordPress qui timeout, et la gestion complète des délais et erreurs dans HTTP API de WordPress : wp_remote_get, timeouts et gestion d’erreurs.

7. Sans accès à la ligne de commande

Sur un mutualisé, ouvrez « Outils > Santé du site » pour lire le détail des tests, désactivez temporairement les extensions de sécurité et de maintenance (renommez leur dossier dans wp-content/plugins/ par SFTP) puis relancez le test. Si l’erreur persiste, communiquez à l’hébergeur le message exact et l’heure : lui seul peut lever un blocage réseau sortant.

Prévenir l’erreur

  • Donnez un délai explicite à chaque appel sortant et traitez l’erreur : un site ne doit jamais attendre sans fin un service tiers.
  • Mettez en cache (transients) les réponses d’API pour réduire le nombre d’appels sortants.
  • Remplacez WP-Cron par un cron système sur les sites qui comptent sur leurs tâches planifiées.
  • Surveillez la Santé du site après chaque changement d’hébergement, de pare-feu ou de CDN.
  • Documentez les hôtes que votre site doit pouvoir joindre pour que le pare-feu les autorise.
« cURL error 28 » veut-il dire que mon site est piraté ?

Non. C’est un délai dépassé sur une requête sortante, généralement dû au réseau, au DNS ou à un service lent. Un piratage ne se manifeste pas par ce message.

Pourquoi le test de requête de bouclage échoue-t-il alors que mon site fonctionne ?

Les visiteurs arrivent de l’extérieur, alors que le bouclage part du serveur et revient vers lui-même. Un pare-feu, un domaine dirigé vers un CDN injoignable depuis le serveur ou un mot de passe HTTP bloquent cette boucle sans gêner les visiteurs.

Quelle différence avec « cURL error 6 » ou « cURL error 7 » ?

Les codes cURL varient avec la cause : 6 signifie que le nom d’hôte n’a pas pu être résolu, 7 que la connexion a été refusée, 28 que le délai est dépassé. Le 28 indique un serveur qui n’a pas répondu à temps, pas un serveur qui a répondu non.

Puis-je augmenter le délai pour tout le site ?

Oui, avec le filtre http_request_timeout, mais c’est déconseillé : chaque appel lent bloquera la page plus longtemps. Réglez plutôt l’hôte concerné, comme dans la solution 6.