Contenu mixte WordPress après le passage en https
Supprimez les avertissements de contenu mixte WordPress après un passage en https : trouvez les URL http:// restées en base de données, remplacez-les sans rien casser et forcez https dans .htaccess.
Publié
Votre certificat est installé, le site se charge en https, et le cadenas refuse toujours d’apparaître. C’est du contenu mixte : la page elle-même est arrivée en https, mais quelque chose à l’intérieur — une image, une feuille de style, un script — est encore demandé en http:// simple. Cet article détaille comment trouver ces URL, les remplacer sans corrompre les données sérialisées, corriger siteurl et home, et forcer https au niveau du serveur pour que le problème ne puisse pas revenir.
Ce qu’est vraiment le contenu mixte
Installer un certificat change la façon dont la connexion est chiffrée. Cela ne change pas ce que vos pages demandent. Chaque http://yoursite.com/wp-content/uploads/logo.png écrit dans un article, un widget ou un réglage de thème avant la migration est toujours dans la base de données, et le navigateur la demande docilement sur une connexion non sécurisée.
Les navigateurs traitent deux catégories différemment, et la distinction compte quand vous évaluez l’urgence :
- Contenu mixte actif — scripts, feuilles de style, iframes, XHR. Bloqué purement et simplement. C’est pourquoi un site peut sembler cassé après un passage en https sans le moindre message d’erreur : une feuille de style a été refusée en silence.
- Contenu mixte passif — images, audio, vidéo. Généralement chargé quand même, mais le cadenas est dégradé et certains navigateurs affichent un indicateur « non sécurisé ».
Les deux méritent d’être corrigés. Seul le premier casse des choses.
Étape 1 : identifier ce qui reste non sécurisé
Commencez par le navigateur. Ouvrez la page, ouvrez les outils de développement et lisez la console. Chaque requête bloquée ou dégradée y est nommée avec son URL complète. Cela vous dit ce qui est non sécurisé ; cela ne vous dit pas où c’est stocké.
Pour cela, regardez directement la base de données. Exportez-la et cherchez dans le fichier :
wp db export dump.sql
grep -o "http://example\.com[^\"']*" dump.sql | sort -u | head -50
Si WP-CLI n’est pas disponible, mysqldump produit le même fichier :
mysqldump -u USER -p DBNAME > dump.sql
Lisez les URL uniques qui remontent. Les chemins d’uploads pointent vers le contenu et les métadonnées des articles. Les chemins de ressources du thème pointent généralement vers les options. Tout ce qui porte un domaine que vous ne reconnaissez pas est une intégration externe, qui demande une autre solution (voir plus bas).
Étape 2 : corriger d’abord siteurl et home
siteurl et home sont les deux options que WordPress utilise pour construire presque toutes les URL internes qu’il génère. Si l’une des deux indique encore http://, WordPress continuera d’émettre des URL non sécurisées quelle que soit la propreté du reste de la base.
Vérifiez-les :
wp option get siteurl
wp option get home
Ou en sql :
SELECT option_name, option_value
FROM wp_options
WHERE option_name IN ('siteurl', 'home');
Ces deux valeurs sont de simples chaînes, pas des tableaux sérialisés : une mise à jour sql directe est donc réellement sans danger ici :
UPDATE wp_options
SET option_value = REPLACE(option_value, 'http://example.com', 'https://example.com')
WHERE option_name IN ('siteurl', 'home');
Un piège avant d’y passer du temps. Si wp-config.php définit WP_HOME ou WP_SITEURL, ces constantes écrasent complètement la base de données et votre mise à jour semblera n’avoir aucun effet :
define( 'WP_HOME', 'https://example.com' );
define( 'WP_SITEURL', 'https://example.com' );
Modifiez-les là, ou supprimez-les et laissez la base décider. Vérifiez cela en premier — cela explique beaucoup de cas du type « je l’ai changé et rien ne s’est passé ».
Étape 3 : le remplacement doit respecter la sérialisation
Tout le reste de la base de données est là où se situe le vrai risque, et ce n’est pas celui auquel on s’attend. Le danger n’est pas que le remplacement rate des URL. C’est qu’il réussisse au niveau du texte et détruise les données autour.
WordPress stocke tableaux et objets sous forme de chaînes sérialisées PHP, et ce format enregistre la longueur en octets de chaque chaîne qu’il contient :
a:1:{s:4:"logo";s:38:"http://example.com/img/logo.png";}
Remplacez http:// par https:// avec un REPLACE sql brut et le texte gagne un octet tandis que la longueur déclarée reste à l’ancienne valeur. PHP lit la longueur, avance d’autant d’octets, ne trouve pas le terminateur là où il l’attend, et refuse de désérialiser tout le tableau. WordPress transmet alors au thème ou à l’extension une valeur qui se comporte comme si le réglage n’avait jamais été enregistré.
Le symptôme n’est pas une erreur. Ce sont des widgets qui disparaissent, des réglages de personnalisation qui se réinitialisent et des mises en page de constructeurs de pages qui s’affichent vides — sans rien dans l’administration pour signaler le problème. Récupérer de cela sans sauvegarde est réellement pénible, alors faites-en une :
wp db export backup-before-https-replace.sql
Utilisez ensuite un outil qui désérialise, remplace à l’intérieur de la structure décodée et re-sérialise avec les longueurs corrigées. Simulation d’abord :
wp search-replace 'http://example.com' 'https://example.com' --dry-run --all-tables --skip-columns=guid
Lisez le rapport. Si une table que vous ne reconnaissez pas affiche des milliers d’occurrences, arrêtez-vous et regardez avant de valider. Puis exécutez pour de vrai :
wp search-replace 'http://example.com' 'https://example.com' --all-tables --skip-columns=guid --report-changed-only
--skip-columns=guid a son importance : le guid est un identifiant permanent pour les lecteurs de flux, pas une URL vivante, et le réécrire peut faire voir à vos abonnés toutes vos archives comme des articles neufs. Si vous voulez savoir quelles colonnes sont sûres en sql brut et lesquelles exigent un traitement conscient de la sérialisation avant de lancer quoi que ce soit, construisez les requêtes avec l’outil de rechercher-remplacer sql pour WordPress.
Si vous ne pouvez pas utiliser la ligne de commande, une extension de migration qui indique explicitement gérer les données sérialisées fait le même travail de décodage depuis l’administration. Si un outil ne le dit pas, considérez qu’il ne le fait pas.
Étape 4 : forcer https au niveau du serveur
Nettoyer la base empêche vos pages de demander des ressources non sécurisées. Cela n’empêche pas un visiteur d’arriver en http:// en premier lieu. C’est une redirection au niveau serveur, et sous Apache elle a sa place dans .htaccess :
# BEGIN Force HTTPS
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
# END Force HTTPS
Deux points sur l’emplacement. Placez ceci au-dessus des marqueurs # BEGIN WordPress — tout ce qui se trouve à l’intérieur est jeté chaque fois que quelqu’un enregistre les permaliens. Et cela ne fonctionne que si mod_rewrite est activé et que l’hôte virtuel autorise les surcharges (AllowOverride All, ou au minimum FileInfo). Sous nginx, .htaccess est totalement ignoré et ceci doit aller dans un bloc server.
Un RedirectMatch ne fera pas ce travail. Il ne correspond qu’au chemin de la requête et n’a aucun moyen de tester si la requête courante est déjà sécurisée : il redirige donc les requêtes https vers elles-mêmes. La RewriteRule conditionnelle ci-dessus est le bon outil.
La boucle de redirection, et pourquoi elle arrive
Si le site se met à rediriger indéfiniment dès que vous ajoutez cette règle, votre TLS se termine quelque part en amont — un répartiteur de charge, un proxy inverse ou un CDN — qui transmet ensuite du http simple à Apache. Apache voit une requête non sécurisée, redirige vers https, le proxy retransmet du http, et c’est reparti.
Testez plutôt l’en-tête transmis :
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
WordPress lui-même a le même angle mort dans cette configuration — is_ssl() lit $_SERVER['HTTPS'], que le proxy ne définit jamais, donc les URL d’administration sortent en http://. Ajoutez ceci à wp-config.php, au-dessus de la ligne « stop editing » :
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
$_SERVER['HTTPS'] = 'on';
}
Soyons clairs sur le risque : X-Forwarded-Proto est un en-tête fourni par le client. Lui faire confiance n’est correct que si un proxy que vous contrôlez l’écrase systématiquement. Sur un serveur joignable directement depuis internet, il peut être falsifié.
Ce qu’un remplacement en base ne corrigera pas
- URL codées en dur dans des fichiers. Tout ce qui est écrit dans
functions.php, un gabarit de thème enfant ou un chemin de ressource en ligne n’est pas dans la base. Faites un grep séparé sur le répertoire du thème. - JSON échappé. Certains constructeurs stockent les URL sous la forme
https:\/\/example.com. Une recherche sur la forme normale les rate complètement : une seconde passe sur la chaîne échappée peut être nécessaire. - Ressources externes. Un script ou une intégration tierce disponible uniquement en http ne peut pas être corrigé de votre côté. Trouvez une version https ou abandonnez-la.
- Caches. Le cache de page, le cache objet et les copies du CDN continuent de servir l’ancien balisage après un remplacement correct. Videz les trois avant de conclure que le remplacement a échoué.
Une chose à éviter : l’en-tête Content Security Policy upgrade-insecure-requests fera taire les avertissements en réécrivant les requêtes non sécurisées dans le navigateur. Il traite le symptôme et laisse les mauvaises URL dans votre base, où le prochain export, la prochaine migration ou le prochain flux les emportera. Corrigez les données, puis utilisez-le comme filet de sécurité si vous le souhaitez.
Toujours bloqué ?
Revérifiez siteurl et home après chaque étape — les extensions et les outils de migration les réécrivent parfois dans votre dos. Si la console nomme encore une URL non sécurisée introuvable dans le fichier exporté, affichez le code source de la page et cherchez-la là : si elle apparaît dans le balisage généré mais pas dans la base de données, c’est qu’elle est construite en PHP, et c’est le thème ou l’extension qui la produit qu’il faut corriger.
FAQ
Questions
Pourquoi mon site WordPress affiche-t-il encore des avertissements de contenu mixte après l'installation d'un certificat SSL ?
Le certificat ne change que la façon dont la connexion est chiffrée, pas ce que vos pages demandent. Les anciennes URL http:// restent stockées en base de données, dans le contenu des articles, les options des widgets et les réglages du thème. Le navigateur charge la page en https, voit une image ou un script demandé en http simple, et signale du contenu mixte sur une page par ailleurs sécurisée.
Comment trouver les URL http:// encore présentes dans ma base de données WordPress ?
Ouvrez une page dans un navigateur et lisez la console de développement, qui nomme chaque requête non sécurisée avec son URL. Exportez ensuite la base de données et cherchez votre domaine avec le préfixe http dans le fichier de sauvegarde. Compter les occurrences par table vous dit si le problème se situe dans le contenu des articles, dans wp_options, ou dans des tables d'extensions auxquelles vous ne pensiez pas.
Puis-je corriger le contenu mixte avec un simple rechercher-remplacer en sql ?
Uniquement pour les valeurs scalaires comme siteurl et home. Tout ce que WordPress stocke sous forme de tableau sérialisé, ce qui couvre la plupart des valeurs d'options et de métadonnées, enregistre la longueur en octets de chaque chaîne qu'il contient. Un remplacement brut modifie le texte mais laisse l'ancienne longueur, la valeur devient impossible à désérialiser, et le réglage revient silencieusement à sa valeur par défaut.
Quelle règle .htaccess force https dans WordPress ?
Une RewriteRule protégée par une condition sur la variable HTTPS, placée au-dessus des marqueurs BEGIN WordPress pour que l'enregistrement des permaliens ne l'efface pas. Derrière un proxy ou un CDN, la variable HTTPS vaut off même sur des requêtes sécurisées : testez plutôt l'en-tête X-Forwarded-Proto, sinon la règle redirigera indéfiniment.
Pourquoi mon site est-il parti en boucle de redirection après le forçage de https ?
Votre TLS se termine presque certainement au niveau d'un proxy ou d'un CDN qui transmet ensuite du http simple à Apache. La réécriture voit une requête non sécurisée, redirige vers https, le proxy retransmet du http, et la boucle recommence. Basculez la condition sur l'en-tête X-Forwarded-Proto et définissez la variable serveur HTTPS dans wp-config.php.
Le contenu mixte casse-t-il toute la page ou seulement le cadenas ?
Cela dépend de la ressource. Les navigateurs bloquent purement et simplement le contenu mixte actif comme les scripts, les feuilles de style et les iframes, ce qui peut casser la mise en page ou les fonctionnalités sans aucune explication visible. Le contenu mixte passif comme les images et la vidéo se charge généralement, mais le cadenas est dégradé et les visiteurs peuvent voir un indicateur non sécurisé. Les deux méritent d'être corrigés.