Bibliothèque de médias WordPress vide : chemins de base de données et problèmes d’URL à vérifier

Cet article vous a aidé ?

Rien ne monte votre tension aussi vite que d’ouvrir la bibliothèque de médias WordPress et de voir… une grille vide. Pas « quelques vignettes manquantes ». Pas « quelques images cassées ». Juste le vide. Pendant ce temps, vous savez que les fichiers existent sur le disque, ou sur S3, ou quelque part où votre facture dit qu’ils devraient être.

Cette panne est en général ennuyeuse. Elle est aussi généralement réparable — si vous arrêtez de deviner et commencez à vérifier les URLs exactes que WordPress génère, les chemins exacts où il pense que les « uploads » résident, et si votre serveur web peut réellement délivrer ces octets.

Playbook de diagnostic rapide

Si vous êtes en astreinte, vous n’avez pas le temps pour une discussion philosophique sur la conception des CMS. Faites ceci dans l’ordre. Chaque étape réduit l’espace de recherche et évite des « correctifs » qui mutent les données de production à l’aveugle.

Premier point : est-ce une illusion d’interface ou WordPress ne trouve-t-il vraiment pas les pièces jointes ?

  1. Vérifiez le nombre de pièces jointes dans la base de données. Si la table posts contient encore des attachments, la bibliothèque n’est pas « vide », elle échoue à rendre les vignettes ou les requêtes sont filtrées.
  2. Vérifiez un enregistrement d’attachement. Confirmez le chemin de fichier stocké dans _wp_attached_file et l’URL de site utilisée pour construire l’URL finale.

Deuxième point : les URLs sont-elles générées incorrectement ?

  1. Confirmez home et siteurl. Un décalage (http vs https, www vs sans-www, domaine erroné) casse les URLs médias et les appels Ajax admin de manière créative.
  2. Vérifiez upload_path et upload_url_path. Ces options peuvent outrepasser les valeurs par défaut de WordPress et survivre aux migrations comme un héritage maudit.

Troisième point : le serveur web peut-il servir les fichiers à ces URLs ?

  1. Prober un fichier réel avec curl -I. Si vous obtenez 403/404/500, vous déboguez Nginx/Apache/CDN/stockage, pas WordPress.
  2. Vérifiez les permissions du système de fichiers et les contraintes SELinux/AppArmor. « Mais c’est 755 » n’est pas une phrase complète.

Quatrième point : externalisez-vous les médias (S3/stockage objet/CDN) et le plugin ment-il ?

  1. Identifiez les plugins d’offload et leurs paramètres. Beaucoup réécrivent les URLs dynamiquement ; certains stockent des URLs canoniques dans postmeta.
  2. Vérifiez si le bucket offload contient réellement les objets. WordPress peut avoir raison et le stockage être vide.

Ce n’est qu’après ces vérifications que vous envisagerez un search/replace en masse dans la base de données. C’est une tronçonneuse. Utilisez-la, mais ne la jonglez pas.

Comment la bibliothèque de médias fonctionne réellement (et comment elle vous trompe)

La bibliothèque de médias WordPress n’est pas un explorateur de fichiers. C’est une vue de base de données. Plus précisément : c’est une liste de posts où post_type = 'attachment', plus des métadonnées qui indiquent à WordPress où le fichier devrait se trouver par rapport à une base « uploads ».

Lorsque vous téléversez une image, WordPress stocke typiquement :

  • Un post d’attachement dans wp_posts avec un titre, un type MIME, et un GUID (historiquement utilisé comme « identifiant unique », souvent abusé comme champ d’URL).
  • Le chemin de fichier relatif dans wp_postmeta avec la clé meta _wp_attached_file (exemple : 2025/12/photo.jpg).
  • Éventuellement des métadonnées d’attachement dans _wp_attachment_metadata (tailles, dimensions, vignettes).

Puis WordPress génère une URL publique en combinant une URL de base (généralement dérivée de home/siteurl plus /wp-content/uploads) avec le chemin relatif de _wp_attached_file. Si l’une de ces entrées est incorrecte, vous vous retrouvez avec des enregistrements de base de données valides qui pointent vers des URLs mortes.

Deux conséquences importantes en production :

  • Vos fichiers peuvent exister et pourtant « ne pas s’afficher ». Une mauvaise URL de base produit des 404 ; la grille de la bibliothèque de médias semble vide parce que les vignettes ne se chargent jamais.
  • Votre base de données peut être correcte et pourtant « ne pas s’afficher ». Si le serveur web ne peut pas lire wp-content/uploads, vous obtiendrez des 403 et des vignettes cassées. WordPress affichera docilement rien et attendra que vous découvriez que le système d’exploitation existe.

Une citation pour rester terre à terre quand vous êtes tenté de « simplement redémarrer la chose » : Espérer n’est pas une stratégie. —Chris Snook

Petite blague #1 : Dépanner WordPress, c’est comme faire de la détection, sauf que le suspect est toujours le DNS et qu’il a un alibi.

Faits et contexte utiles (pour arrêter d’être surpris)

  1. Les attachments WordPress sont des posts. Les éléments médias vivent dans wp_posts en tant que post_type=attachment ; c’est pourquoi la corruption de la base ou un filtrage peuvent « effacer » une bibliothèque sans toucher au disque.
  2. Le champ GUID est historiquement surchargé. WordPress utilisait à l’origine GUID comme identifiant unique (pensez RSS). Beaucoup de thèmes/plugins le traitent comme l’URL du fichier, ce qui transforme les migrations en chaos lent.
  3. Le chemin uploads est devenu configurable tôt — et reste collant. Les options upload_path et upload_url_path peuvent persister après des mises à niveau et des migrations, même si elles ne correspondent plus à la réalité.
  4. Les dossiers année/mois étaient un choix de performance et d’organisation. La structure par défaut uploads/2025/12/ réduit l’engorgement des répertoires ; la désactiver peut créer des millions de fichiers dans un seul dossier et irriter les systèmes de fichiers.
  5. La régénération des vignettes est devenue courante parce que les métadonnées vieillissent mal. Changez de thème, de tailles ou de traitement d’image et votre _wp_attachment_metadata peut ne plus correspondre à ce qui est sur le disque.
  6. Les plugins d’offload ont changé le domaine de défaillance. Une fois que vous réécrivez les URLs vers S3/CDN, votre instance WordPress peut être saine alors que votre bucket applique silencieusement des refus.
  7. Les échecs admin-ajax et REST peuvent ressembler à une « bibliothèque vide ». La grille charge les données via des appels JS ; CSP, contenu mixte ou endpoints bloqués peuvent provoquer une interface vide sans problème de base de données.
  8. Les CDNs peuvent mettre en cache vos erreurs. Une courte période d’URLs erronées peut être mise en cache comme 404/403, et « réparer WordPress » ne corrige pas l’expérience utilisateur tant que vous n’avez pas purgé.

Les vrais modes de panne : URLs BD, chemins, réécritures, stockage

1) L’URL du site est incorrecte (ou incorrecte de manière inconsistante)

Classique : vous avez migré de staging vers production, basculé HTTPS, ajouté www, ou placé derrière un reverse proxy. WordPress stocke des URLs dans la base, mais les dérive aussi à l’exécution. Si home et siteurl ne correspondent pas à la réalité que voient les utilisateurs, vous obtenez du contenu mixte, des URLs médias erronées, et des écrans d’administration qui se comportent comme un distributeur automatique hanté.

À quoi ressemble le « vide » ici :

  • La bibliothèque de médias charge mais les vignettes sont blanches (le réseau montre 404/301 boucles/contenu mixte bloqué).
  • Cliquer sur une pièce jointe affiche une icône d’image cassée.
  • La console du navigateur montre des requêtes bloquées, des erreurs CORS, ou des redirections répétées.

2) Les overrides upload_path / upload_url_path vous empoisonnent

La plupart des sites ne définissent pas ces options. Ceux qui le font les oublient souvent. Après une migration, WordPress peut encore penser que les uploads sont à /var/www/oldsite/uploads ou que les URLs publiques commencent par un domaine abandonné. Les fichiers peuvent être correctement situés dans wp-content/uploads, mais WordPress cherche ailleurs.

3) Les enregistrements de la base sont présents, mais les fichiers manquent (ou inversement)

Les sauvegardes et restaurations restaurent souvent une seule partie de ce couple. Les gens restaurent la base mais pas wp-content/uploads. Ou ils rsyncent les uploads mais pas la base. Dans les deux cas, la bibliothèque devient soit un musée de références cassées soit un tas de fichiers non référencés que vous payez pour stocker.

4) Le routage du serveur web bloque /wp-content/uploads

Règles Nginx trop zélées, un .htaccess Apache qui nie, un plugin de sécurité renvoyant des 403, ou une règle WAF qui trouve les JPG suspects. Si vous ne pouvez pas récupérer un fichier connu avec curl, ce n’est pas un problème WordPress. C’est un problème de livraison.

5) Permissions, propriété, SELinux/AppArmor

Uploads lisibles par root mais pas par l’utilisateur du serveur web. Ou lisibles, mais le parcours est bloqué à cause des bits d’exécution sur les répertoires. Ou les étiquettes SELinux empêchent httpd de lire le chemin. Ces problèmes se manifestent en 403 (parfois 404 si le serveur masque les échecs d’autorisation).

6) Offload / CDN / stockage objet mal configurés

Les plugins d’offload :

  • réécrivent les URLs à la volée (donc la BD semble « correcte » mais les requêtes vont vers S3/CDN), ou
  • stockent des URLs cloud dans postmeta / GUID / tables personnalisées.

Échec courant : identifiants pivotés, politique de bucket modifiée, endpoint de région changé, ou objets privés servis sans URLs signées. L’administration WordPress n’affiche rien parce que les vignettes sont récupérées depuis le CDN et échouent.

7) L’UI de la bibliothèque de médias échoue, pas les médias

Si la requête de liste d’attachements est correcte mais que les requêtes JavaScript échouent, la grille peut rester vide. Causes : en-têtes CSP, blocage de admin-ajax.php, problèmes d’authentification REST API, ou un plugin qui casse l’administration uniquement sur l’écran médias avec une erreur fatale.

Petite blague #2 : La bibliothèque de médias n’a pas disparu ; elle est juste partie sur un nouveau domaine sans prévenir la comptabilité.

Tâches pratiques : commandes, sorties et décisions

Voici les tâches que j’exécute réellement quand quelqu’un envoie « bibliothèque médias vide » dans le chat. Chacune inclut (1) une commande, (2) un exemple de sortie, et (3) quelle décision en tirer.

Task 1: Count attachments in the database

cr0x@server:~$ mysql -N -e "SELECT COUNT(*) FROM wp_posts WHERE post_type='attachment';"
12487

Ce que cela signifie : Les attachments existent dans la BD. Si l’UI est vide, vous avez probablement un problème de génération d’URL, des requêtes de vignettes, des échecs JS admin, ou un filtre de requête.

Décision : Ne restaurez pas encore des backups. Passez à la vérification des URL/chemins et aux vérifications navigateur/réseau.

Task 2: Fetch a sample attachment meta (file path)

cr0x@server:~$ mysql -N -e "SELECT p.ID, p.post_title, pm.meta_value FROM wp_posts p JOIN wp_postmeta pm ON pm.post_id=p.ID WHERE p.post_type='attachment' AND pm.meta_key='_wp_attached_file' ORDER BY p.ID DESC LIMIT 1;"
88211	hero-banner	2025/12/hero-banner.jpg

Ce que cela signifie : WordPress attend le fichier dans uploads/2025/12/hero-banner.jpg sous ce qu’il considère comme le répertoire de base des uploads.

Décision : Validez que le fichier existe sur le disque ou dans le stockage offload, et validez l’URL publique construite pour lui.

Task 3: Check home and siteurl options

cr0x@server:~$ mysql -N -e "SELECT option_name, option_value FROM wp_options WHERE option_name IN ('home','siteurl');"
home	https://www.example.com
siteurl	https://example.com

Ce que cela signifie : Mismatch. Parfois c’est intentionnel (WordPress dans un sous-répertoire), souvent c’est accidentel. Cela peut casser les appels admin et la génération d’URLs médias.

Décision : Décidez du domaine canonique (avec ou sans www, https). Alignez ces valeurs, ou implémentez les headers proxy et règles de réécriture appropriés si la séparation est volontaire.

Task 4: Check uploads overrides in wp_options

cr0x@server:~$ mysql -N -e "SELECT option_name, option_value FROM wp_options WHERE option_name IN ('upload_path','upload_url_path','uploads_use_yearmonth_folders');"
upload_path	/var/www/legacy/uploads
upload_url_path	https://legacy.example.net/uploads
uploads_use_yearmonth_folders	1

Ce que cela signifie : WordPress est explicitement configuré pour utiliser un emplacement de filesystem legacy et une URL legacy. Voilà votre « bibliothèque vide ».

Décision : Supprimez ces overrides (laisser vide) ou mettez-les à jour vers le bon chemin/URL. Ne le faites pas à l’aveugle — vérifiez d’abord l’emplacement réel des médias.

Task 5: Determine uploads directory from WordPress config (WP-CLI)

cr0x@server:~$ cd /var/www/html
cr0x@server:~$ wp option get upload_path
/var/www/legacy/uploads

Ce que cela signifie : Confirme l’option BD du Task 4 en utilisant WordPress lui-même (utile quand les préfixes de table diffèrent).

Décision : Si upload_path est incorrect, corrigez-le et retestez une seule récupération d’image avant les opérations en masse.

Task 6: Verify a known file exists on disk

cr0x@server:~$ ls -lah /var/www/html/wp-content/uploads/2025/12/hero-banner.jpg
-rw-r--r-- 1 www-data www-data 482K Dec 26 09:13 /var/www/html/wp-content/uploads/2025/12/hero-banner.jpg

Ce que cela signifie : Le fichier existe, lisible, appartenant à l’utilisateur du serveur web (ou du moins lisible par le groupe). Bien.

Décision : Si le fichier existe mais que la bibliothèque est vide, passez à la récupération HTTP et aux règles du serveur web. S’il n’existe pas, restaurez les uploads ou réconciliez le stockage offload.

Task 7: Check directory permissions up the tree (the “execute bit” trap)

cr0x@server:~$ namei -l /var/www/html/wp-content/uploads/2025/12/hero-banner.jpg
f: /var/www/html/wp-content/uploads/2025/12/hero-banner.jpg
drwxr-xr-x root     root     /
drwxr-xr-x root     root     var
drwxr-xr-x root     root     www
drwxr-xr-x root     root     html
drwxr-xr-x www-data www-data wp-content
drwxr-x--- root     root     uploads
drwxr-xr-x www-data www-data 2025
drwxr-xr-x www-data www-data 12
-rw-r--r-- www-data www-data hero-banner.jpg

Ce que cela signifie : Le répertoire uploads est drwxr-x--- possédé par root, donc www-data peut ne pas le traverser selon l’utilisateur/groupe du serveur. Voilà un 403 en préparation.

Décision : Corrigez la propriété/permissions de uploads pour que le serveur web puisse traverser et lire. Ne faites pas chmod 777 ; c’est ainsi qu’on rencontre un ransomware professionnellement.

Task 8: If SELinux exists, check enforcement and recent denials

cr0x@server:~$ getenforce
Enforcing
cr0x@server:~$ sudo ausearch -m avc -ts recent | tail -n 5
type=AVC msg=audit(1735142651.812:912): avc:  denied  { read } for  pid=2143 comm="nginx" name="hero-banner.jpg" dev="sda1" ino=550912 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file permissive=0

Ce que cela signifie : SELinux bloque les lectures car le contexte du fichier est incorrect (default_t au lieu d’un type lisible par le web).

Décision : Relabellez les uploads avec le contexte correct et rendez-le persistant. Sinon vous « corrigerez » jusqu’à la prochaine restauration.

Task 9: Probe the media URL via HTTP (bypass the browser)

cr0x@server:~$ curl -I https://www.example.com/wp-content/uploads/2025/12/hero-banner.jpg
HTTP/2 403
date: Sat, 27 Dec 2025 10:12:15 GMT
content-type: text/html
server: nginx

Ce que cela signifie : 403 à la frontière. Ce n’est pas « WordPress a oublié l’image ». C’est une politique, une auth, un WAF, des permissions, ou une règle qui nie /wp-content.

Décision : Inspectez la configuration Nginx/Apache et les couches de sécurité. Ne touchez pas à la BD tant que HTTP ne sert pas des fichiers connus.

Task 10: Check Nginx config for accidental denies on uploads

cr0x@server:~$ sudo nginx -T 2>/dev/null | grep -nE "wp-content|uploads|deny|location"
128:	location ~* /(wp-content|wp-includes)/.*\.php$ { deny all; }
141:	location ~* ^/wp-content/uploads/ { deny all; }

Ce que cela signifie : Quelqu’un a bloqué les uploads. Souvent l’intention est de bloquer l’exécution PHP, mais c’est implémenté comme « deny all » au lieu de « deny PHP ».

Décision : Changez les règles pour autoriser les fichiers statiques tout en refusant le contenu exécutable. Retestez avec curl -I.

Task 11: Check Apache .htaccess or vhost rules affecting uploads

cr0x@server:~$ sudo grep -RIn "uploads\|wp-content" /var/www/html/.htaccess /etc/apache2/sites-enabled 2>/dev/null | head
/var/www/html/.htaccess:12:RewriteRule ^wp-content/uploads/ - [F,L]

Ce que cela signifie : Une règle de réécriture interdit l’accès aux uploads (renvoie 403). C’est soit un malentendu, soit un snippet de sécurité copié-collé appliqué trop largement.

Décision : Corrigez la règle pour ne bloquer que les patterns dangereux (comme le PHP dans uploads), pas tout le répertoire.

Task 12: Check whether requests are being redirected to the wrong host

cr0x@server:~$ curl -I http://www.example.com/wp-content/uploads/2025/12/hero-banner.jpg
HTTP/1.1 301 Moved Permanently
Location: https://example.com/wp-content/uploads/2025/12/hero-banner.jpg

Ce que cela signifie : L’hôte canonique est différent. Si WordPress pense « www » mais votre redirection force sans-www (ou inversement), vous pouvez obtenir du contenu mixte ou des appels admin bloqués selon les cookies et CORS.

Décision : Choisissez un hôte canonique et alignez les options WordPress, les redirections et la configuration CDN avec celui-ci.

Task 13: Confirm attachment GUID patterns (spot stale domains)

cr0x@server:~$ mysql -N -e "SELECT COUNT(*) FROM wp_posts WHERE post_type='attachment' AND guid LIKE '%legacy.example.net%';"
12487

Ce que cela signifie : Chaque attachment a encore un domaine legacy dans le GUID. Cela peut ou non être important selon le comportement des thèmes/plugins.

Décision : Si votre front-end ou un plugin utilise le GUID pour les URLs, vous devez planifier une mise à jour contrôlée. Sinon, vous pouvez le mettre à jour pour la cohérence — mais faites-le prudemment et testez les feeds/intégrations.

Task 14: Search for old domains in postmeta (actual file URL overrides)

cr0x@server:~$ mysql -N -e "SELECT meta_key, COUNT(*) FROM wp_postmeta WHERE meta_value LIKE '%legacy.example.net%' GROUP BY meta_key ORDER BY COUNT(*) DESC LIMIT 10;"
_wp_attached_file	0
amazonS3_info	12487

Ce que cela signifie : Les métadonnées d’offload référencent la configuration de domaine/bucket legacy. C’est souvent le vrai moteur des vignettes cassées quand on utilise des plugins d’offload.

Décision : Corrigez d’abord les paramètres du plugin d’offload ; puis envisagez des outils de migration/ré-hydratation de métadonnées spécifiques à ce plugin.

Task 15: Validate WP-CLI can list attachments (bypass the admin UI)

cr0x@server:~$ wp post list --post_type=attachment --fields=ID,post_title,guid --format=table | head
+------+------------------+--------------------------------------------------------------+
| ID   | post_title       | guid                                                         |
+------+------------------+--------------------------------------------------------------+
| 88199| hero-banner      | https://legacy.example.net/wp-content/uploads/2025/12/hero... |
| 88185| team-headshot    | https://legacy.example.net/wp-content/uploads/2025/12/team... |
+------+------------------+--------------------------------------------------------------+

Ce que cela signifie : WordPress voit bien les attachments au niveau applicatif.

Décision : Si WP-CLI liste les attachments mais que l’UI est vide, concentrez-vous sur le JS admin, les appels API, et les échecs de récupération de vignettes, pas sur des « lignes BD manquantes ».

Task 16: Check admin Ajax/REST endpoints quickly (server-side)

cr0x@server:~$ curl -I https://www.example.com/wp-admin/admin-ajax.php
HTTP/2 302
location: https://www.example.com/wp-login.php?redirect_to=https%3A%2F%2Fwww.example.com%2Fwp-admin%2Fadmin-ajax.php

Ce que cela signifie : L’endpoint est joignable. Un 302 vers login est normal non authentifié. Si vous obtenez 403/500 ici, l’UI de la bibliothèque peut casser.

Décision : Si les endpoints admin échouent, vérifiez les règles WAF, mod_security, les couches de cache, et les erreurs PHP avant de toucher aux URLs médias.

Task 17: Find PHP errors tied to media requests (quick grep)

cr0x@server:~$ sudo tail -n 80 /var/log/php8.2-fpm.log | grep -iE "fatal|wp-admin|media|imagick|gd" | tail -n 10
[27-Dec-2025 10:11:05] PHP Fatal error:  Uncaught Error: Call to undefined function imagewebp() in /var/www/html/wp-includes/media.php:4212

Ce que cela signifie : Une fonctionnalité manquante de la librairie image (ici le support WebP) peut casser la génération de vignettes ou les opérations de métadonnées, parfois en cascade dans des comportements étranges de l’UI.

Décision : Réparez le paquet/extension PHP manquant ou désactivez la fonctionnalité/plugin qui en dépend ; puis régénérez les vignettes si nécessaire.

Task 18: Controlled DB search/replace for domain changes (dry run)

cr0x@server:~$ wp search-replace 'https://legacy.example.net' 'https://www.example.com' --dry-run --all-tables
Success: Made 0 replacements.

Ce que cela signifie : WP-CLI n’a pas trouvé de correspondances (ou vous ne scannez pas les bonnes tables/préfixe). Alternativement, l’ancien domaine est stocké sans scheme, ou dans des tableaux sérialisés sous une forme différente.

Décision : Resondez la recherche (http vs https, avec/sans www), ou ciblez des tables spécifiques. Quand vous l’exécuterez en vrai, prenez d’abord un snapshot BD.

Task 19: Check for mixed-content patterns (http media on https site)

cr0x@server:~$ mysql -N -e "SELECT COUNT(*) FROM wp_posts WHERE post_type='attachment' AND guid LIKE 'http://%';"
12487

Ce que cela signifie : Les attachments référencent encore http. Les navigateurs bloqueront souvent ou avertiront, et les vignettes admin peuvent échouer selon les politiques.

Décision : Planifiez une migration prudente vers des URLs https (ou assurez-vous que WordPress génère https indépendamment de l’usage du GUID).

Trois mini-histoires d’entreprise issues du terrain

Mini-histoire 1 : L’incident causé par une mauvaise hypothèse

Une organisation de taille moyenne a migré un parc WordPress d’une flotte de VM legacy vers des conteneurs. Le plan de migration semblait propre : dump/restore de la base, rsync de wp-content, mise à jour DNS, terminé. La bibliothèque de médias est devenue vide juste après le cutover.

L’équipe a supposé « bibliothèque vide = uploads manquants ». Ils ont donc relancé rsync, deux fois, pendant les heures ouvrables. Puis ils ont commencé à restaurer des backups plus anciens parce que sûrement la copie la plus récente était corrompue. Ils ont perdu une journée et créé un joli backlog d’états incohérents : certaines pages référençaient des médias présents uniquement dans la première restauration, d’autres dans la deuxième.

Le vrai problème était banal : upload_url_path dans wp_options pointait encore vers l’ancien domaine. WordPress générait des URLs de vignettes vers un hostname qui renvoyait maintenant un 301 vers un endpoint interne seulement. Depuis le VPN interne cela « fonctionnait », depuis l’internet non. L’UI admin semblait vide parce que chaque requête de vignette était redirigée vers un endroit que le navigateur ne suivrait pas à cause de cookies mixtes et de cross-origin bloqués.

La correction a été anticlimactique : vider l’option, aligner home/siteurl, purger le CDN, et soudain la bibliothèque « réapparaît ». Les fichiers avaient été présents tout le temps. L’incident venait d’une mauvaise hypothèse : croire que la bibliothèque de médias est une vue directe du système de fichiers. Ce n’est pas le cas.

Mini-histoire 2 : L’optimisation qui s’est retournée contre eux

Une équipe marketing se plaignait que les uploads d’images étaient « lents », un ingénieur a donc introduit un plugin d’offload vers un stockage objet et un CDN devant. Sur le papier : moins d’écritures disque, moins de charge sur l’origine, pages plus rapides. En pratique : une nouvelle dépendance de production avec un rayon d’explosion très excitant.

Quelques semaines plus tard, la bibliothèque de médias affichait des carrés vides. Pas d’icônes cassées — juste vierges, comme si les images n’avaient jamais existé. Le front-end perdait aussi des images, mais pas de façon cohérente. Certains navigateurs fonctionnaient. D’autres non. Le problème était intermittent, ce qui est la façon de l’univers de se moquer.

Cause racine : le CDN avait mis en cache des réponses 403 du stockage objet pour un sous-ensemble de clés. Une mise à jour de politique avait temporairement refusé les lectures pour des objets sans un préfixe spécifique. Le plugin continuait de générer des URLs CDN, et WordPress lui-même n’avait aucune idée que quelque chose n’allait pas. Les vignettes admin provenaient du CDN, qui servait fidèlement des 403 mis en cache.

La « optimisation » avait déplacé la disponibilité des médias de « l’origine peut-elle servir les fichiers ? » vers « la politique CDN est-elle correcte, la politique du bucket est-elle correcte, les identifiants sont-ils valides, le plugin réécrit-il correctement les URLs, et avons-nous purgé le cache après les changements de politique ? » Ils ont corrigé la politique et purgé le CDN, mais la vraie leçon était la gouvernance : traiter l’offload média comme un changement plateforme, pas une installation rapide de plugin.

Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la journée

Une autre boite avait un rituel : chaque migration nécessitait deux vérifications avant de déclarer le succès. D’abord, confirmer le nombre d’attachments et un échantillon d’entrées _wp_attached_file. Ensuite, récupérer dix images aléatoires avec curl -I depuis l’extérieur du réseau (une sonde externe réelle, pas « ça marche sur mon laptop »). Personne n’aimait cette checklist. C’est pour ça qu’elle fonctionnait.

Lors d’un déménagement de datacenter, la bibliothèque de médias semblait vide dans l’admin pour un sous-ensemble d’utilisateurs. Les ingénieurs n’ont pas paniqué. Ils ont exécuté les vérifications routinières. Les comptes BD étaient corrects. La présence des fichiers sur disque était correcte. Les fetchs HTTP depuis l’extérieur renvoyaient 403 — mais seulement pour les requêtes manquant un certain header. Cela a réduit le diagnostic à la couche edge.

Il s’est avéré que le nouveau profil WAF bloquait les requêtes vers /wp-content/uploads/ à moins qu’elles n’aient un User-Agent ressemblant à un navigateur. Le vendeur WAF appelait cela « protection bot ». L’entreprise appelait cela « casser le site ».

Parce qu’ils avaient l’habitude d’exécuter les mêmes tests à chaque fois, ils avaient des preuves claires : « l’origine peut lire les fichiers, WordPress génère le bon chemin, l’edge renvoie 403 ». Ils ont fait corriger la règle rapidement et évité l’erreur classique de réécrire les URLs BD pour poursuivre un problème qui n’était pas lié à la base.

Erreurs fréquentes : symptôme → cause racine → fix

  • Symptôme : La grille de la bibliothèque de médias est vide ; le compte d’attachments semble être zéro dans l’UI.

    Cause racine : Requêtes JS admin échouant (REST/Ajax bloqués par WAF, CSP, plugin de cache, ou erreur fatale sur l’écran médias).

    Fix : Vérifiez les devtools du navigateur réseau/console, puis validez admin-ajax.php et les endpoints REST. Corrigez la règle serveur/WAF ou le conflit de plugin avant de toucher aux chemins d’uploads.

  • Symptôme : Les attachments existent en BD, mais les vignettes sont cassées et renvoient 404.

    Cause racine : Mauvais home/siteurl, ou ancien domaine intégré dans des overrides d’URL.

    Fix : Alignez home et siteurl sur le domaine canonique. Effacez ou corrigez upload_url_path. Purgez le cache CDN après.

  • Symptôme : Les vignettes renvoient 403 depuis /wp-content/uploads.

    Cause racine : Règles de refus du serveur web, permissions de traversal du filesystem, ou contexte SELinux bloquant les lectures.

    Fix : Ajustez les règles Nginx/Apache pour autoriser les fichiers statiques ; corrigez la propriété/permissions ; relabellez les contextes SELinux.

  • Symptôme : Les fichiers existent sur disque mais WordPress téléverse de nouveaux médias dans un répertoire inattendu.

    Cause racine : Override upload_path pointant ailleurs, ou mappage de volume container erroné.

    Fix : Supprimez l’override pour que WordPress utilise wp-content/uploads, ou corrigez le chemin de mount. Confirmez avec un téléversement test.

  • Symptôme : Les médias montrent dans l’admin mais les images sont cassées seulement en front-end.

    Cause racine : CDN met en cache des 404 anciens, problèmes de contenu mixte http/https, ou thème qui construit les URLs à partir du GUID différemment que l’admin.

    Fix : Purgez le CDN, forcez https, et auditez thème/plugins pour usage du GUID. Corrigez la génération d’URL canonique.

  • Symptôme : Après migration, certaines images fonctionnent, les plus récentes non.

    Cause racine : Restauration partielle du répertoire uploads ; sous-arbres année/mois manquants ; ou synchronisation stockage objet incomplète.

    Fix : Comparez les arbres filesystem, restaurez les répertoires manquants, ou relancez la sync stockage objet avec vérification.

  • Symptôme : Cliquer sur une pièce jointe montre des métadonnées mais pas d’aperçu ; redimensionnement échoue.

    Cause racine : GD/Imagick manquant ou erreurs PHP fatales dans le traitement média.

    Fix : Installez/activez les extensions et bibliothèques PHP requises ; puis régénérez les vignettes si les métadonnées sont obsolètes.

  • Symptôme : La bibliothèque de médias montre des éléments mais la recherche/filtre ne retourne rien.

    Cause racine : Plugin altérant les requêtes (hooks) ou problèmes de collation/index BD causant des requêtes lentes/échouées.

    Fix : Désactivez les plugins suspects, vérifiez les logs MySQL slow et les erreurs, réparez les tables/index si nécessaire.

Checklists / plan étape par étape

Checklist A: « La bibliothèque de médias paraît vide » en production

  1. Confirmer que la BD a des attachments. Si zéro, vous avez une perte de données ou une mauvaise connexion à la base / préfixe de table.
  2. Vérifier le _wp_attached_file d’une pièce jointe. Validez qu’il est sain (chemin relatif, pas un chemin absolu legacy bizarre sauf si c’est intentionnel).
  3. Confirmer home et siteurl. Décidez du domaine et du schéma canoniques.
  4. Vérifier upload_path et upload_url_path. Effacez les overrides legacy à moins que vous les utilisiez volontairement.
  5. Récupérer un média connu avec curl -I. Notez 200 vs 301 vs 403 vs 404.
  6. Vérifier l’existence filesystem et les permissions de traversal. Utilisez namei -l pour tout le chemin.
  7. Vérifier SELinux/AppArmor si applicable. Mode Enforcing plus denials AVC égale misère silencieuse.
  8. Vérifier les règles du serveur web. Assurez-vous de bloquer le PHP dans uploads, pas le répertoire entier.
  9. Vérifier CDN/WAF. Cherchez des réponses 403/404 mises en cache et des blocages basés sur chemin.
  10. Ce n’est qu’après cela qu’on fait un search/replace BD. Faites un snapshot d’abord, exécutez des dry runs, et validez la gestion des données sérialisées via WP-CLI.

Checklist B: Remédiation sûre d’URL/chemin sans empirer les choses

  1. Sauvegarde de la base. Dump logique plus snapshot si disponible. Si vous ne pouvez pas restaurer, vous ne pouvez pas « réparer ».
  2. Décidez de l’URL canonique. https vs http, www vs sans-www, et si WordPress vit dans un sous-répertoire.
  3. Corrigez d’abord home/siteurl. Cela influence tout le reste.
  4. Effacez les overrides d’uploads sauf si nécessaires. Laissez WordPress utiliser wp-content/uploads sauf motif précis.
  5. Validez un attachement bout-à-bout. Enregistrement BD → filesystem/objet → HTTP 200.
  6. Exécutez wp search-replace avec --dry-run. Confirmez la portée et les tables ciblées.
  7. Faites la vraie remplace en heures creuses. Mesurez la durée, l’impact de verrouillage, et ayez un rollback prêt.
  8. Purgez les caches CDN. Sinon votre « correctif » est invisible.
  9. Régénérez les vignettes si les tailles ont changé. Seulement si vous avez confirmé que les originaux sont accessibles.

Checklist C: Renforcement pour éviter que cela ne se reproduise

  1. Surveillez le HTTP pour des objets médias échantillons. Une sonde synthétique qui récupère quelques uploads connus détecte tôt les 403/404.
  2. Suivez le nombre d’attachments et la taille de l’arbre uploads. De grandes divergences indiquent des restaurations partielles ou des pipelines cassés.
  3. Documentez l’usage d’offload/CDN. Traitez-le comme une infrastructure critique avec contrôle des changements.
  4. Standardisez la gestion du domaine canonique. Headers proxy, redirections et réglages WordPress doivent être cohérents.
  5. Interdisez les search/replace SQL ad-hoc en prod. Utilisez WP-CLI pour la sécurité des sérialisations et l’auditabilité.

FAQ

Pourquoi la bibliothèque de médias est vide alors que les articles affichent encore des images ?

Souvent le front-end rend du HTML mis en cache ou utilise une source d’URL différente (thème codant en dur, URLs CDN), tandis que la grille admin dépend d’appels JS et d’endpoints de vignette qui sont bloqués.

Est-il sûr de mettre à jour home et siteurl directement dans la base de données ?

Oui, si vous connaissez l’URL canonique correcte et disposez d’un rollback. Faites-le délibérément ; les mismatches peuvent casser les cookies de connexion et l’accès admin. Préférez WP-CLI quand c’est possible pour un comportement cohérent.

Quelle est la différence entre home et siteurl ?

home est l’adresse publique du site. siteurl est l’endroit où résident les fichiers core de WordPress. Ils sont souvent identiques, sauf dans les installations en sous-répertoire ou les configurations particulières. Les différences accidentelles provoquent des comportements étranges.

Faut-il « corriger » les GUID des attachments lors d’une migration ?

Parfois. WordPress ne s’appuie pas sur le GUID pour les URLs médias dans le chemin propre, mais les plugins et thèmes peuvent le faire. Si vous voyez le GUID utilisé dans des templates, ou des feeds/intégrations qui en dépendent, planifiez une mise à jour soigneuse.

Pourquoi ai-je un 403 en demandant des fichiers dans wp-content/uploads ?

Soit la configuration du serveur web nie ce chemin, soit les permissions filesystem bloquent le parcours/la lecture, soit SELinux/AppArmor empêche l’accès. Ne supposez pas que c’est un bug WordPress ; prouvez-le avec curl -I et les logs.

Après avoir corrigé les URLs, pourquoi vois-je encore des vignettes manquantes ?

Les CDNs et les navigateurs mettent en cache les échecs. Aussi, les vignettes peuvent ne pas exister sur le disque si vous avez restauré seulement les originaux ou changé les tailles d’images. Purgez les caches, puis régénérez les vignettes si les originaux sont accessibles.

Comment savoir si un plugin d’offload est impliqué ?

Cherchez des paramètres de plugin liés à S3/stockage objet, vérifiez postmeta pour des clés comme amazonS3_info, et inspectez les URLs des vignettes dans le panneau réseau du navigateur. Si elles pointent vers un domaine CDN/bucket, vous utilisez un offload.

Un reverse proxy peut-il causer une « bibliothèque de médias vide » ?

Oui. Si WordPress ne voit pas le schéma/hôte correct (headers proxy manquants), il peut générer des URLs http sur un site https, déclenchant du contenu mixte ou des boucles de redirection qui empêchent le chargement des vignettes.

Quelle est la faute de migration la plus courante ?

Restaurer la base sans restaurer wp-content/uploads (ou restaurer les uploads sans la base). Les médias sont un système couplé : métadonnées + octets.

Conclusion : prochaines étapes qui réduisent vraiment le risque

Si votre bibliothèque de médias WordPress paraît vide, résistez à l’envie de « réinstaller WordPress » ou de lancer des search/replace au hasard en prod. La correction est généralement un décalage entre ce que WordPress pense être l’URL/chemin des uploads et ce que votre infrastructure sert réellement.

Faites ceci ensuite :

  1. Prouvez que les attachments existent en BD et récupérez un exemple _wp_attached_file.
  2. Alignez home/siteurl et supprimez ou corrigez upload_path/upload_url_path.
  3. Récupérez un upload connu via curl -I et corrigez l’edge/serveur web/permissions jusqu’à obtenir un 200 propre.
  4. Si l’offload est en jeu, validez les permissions bucket/CDN et purgez les 403/404 mis en cache.
  5. Ce n’est qu’alors que vous lancerez un search/replace contrôlé via WP-CLI et, si nécessaire, la régénération des vignettes.

Rendez cela ennuyeux : ajoutez une petite vérification synthétique qui récupère une poignée d’uploads connus. Le meilleur moment pour découvrir que vos uploads sont bloqués est jamais.

← Précédent
MySQL vs Percona Server : trouver les requêtes tueuses sans tâtonner
Suivant →
Résoudre l’erreur « Authentication Failed » de Proxmox PBS : jetons, autorisations et empreintes

Laisser un commentaire