Бібліотека медіа WordPress виглядає порожньою: перевірте шляхи БД та URL

Було корисно?

Нічого так не підвищує тиск, як відкриття Бібліотеки медіа WordPress і побачене… порожню сітку. Не «кілька відсутніх ескізів». Не «деякі зламані зображення». Просто порожнеча. При цьому ви знаєте, що файли існують на диску, або в S3, або десь, за що вам виставляють рахунок.

Ця помилка зазвичай нудна. І зазвичай її можна виправити — якщо ви припините гадати й почнете перевіряти точні URL, які генерує WordPress, точні шляхи, які він вважає «uploads», і чи може ваш вебсервер справді віддавати ці байти.

Швидка методика діагностики

Якщо ви на виклику, у вас немає часу на філософські дискусії про дизайн CMS. Робіть це в порядку. Кожен крок звужує простір пошуку і уникає «виправлень», які бездумно змінюють продакшн-дані.

По-перше: це ілюзія інтерфейсу чи WordPress справді не знаходить вкладень?

  1. Перевірте кількість вкладень у базі даних. Якщо таблиця posts все ще має вкладення, бібліотека не «порожня», просто не відображає ескізи або запити фільтруються.
  2. Перевірте одну запис вкладення. Підтвердіть збережений шлях файлу у _wp_attached_file і URL сайту, який використовується для побудови фінального URL.

По-друге: чи генеруються URL неправильно?

  1. Підтвердіть значення home та siteurl. Розбіжність (http vs https, www vs без www, неправильний домен) ламає медіа-URL та адмін Ajax виклики по-креативному.
  2. Перевірте upload_path і upload_url_path. Ці опції можуть перевизначати стандартні шляхи WordPress і «виживати» після міграцій, немов проклята спадщина.

По-третє: чи може вебсервер віддавати файли за цими URL?

  1. Пропробуйте реальний файл через curl -I. Якщо ви отримуєте 403/404/500, ви дебагуєте Nginx/Apache/CDN/сховище, а не WordPress.
  2. Перевірте права файлової системи та обмеження SELinux/AppArmor. «Але це 755» — це не повне речення.

По-четверте: чи ви відвантажуєте медіа (S3/об’єктне сховище/CDN) і плагін брешe?

  1. Ідентифікуйте плагіни offload та їхні налаштування. Багато хто динамічно переписує URL; деякі зберігають канонічні URL у postmeta.
  2. Перевірте, чи бакет offload справді містить об’єкти. WordPress може бути правий, а сховище — порожнім.

Лише після цього розглядайте масове search/replace у базі. Це бензопила. Користуйтеся нею, але не жонглюйте нею.

Як насправді працює Бібліотека медіа (і як вона вас обманює)

Бібліотека медіа WordPress — це не файловий браузер. Це подання бази даних. Зокрема: це список записів у яких post_type = 'attachment', плюс метадані, які кажуть WordPress, де файл має знаходитись відносно бази uploads.

Коли ви завантажуєте зображення, WordPress зазвичай зберігає:

  • Запис attachment post у wp_posts з назвою, MIME-типом та GUID (історично використовувався як «унікальний ідентифікатор», часто застосовувався як поле URL).
  • Відносний шлях файлу у wp_postmeta з ключем мети _wp_attached_file (приклад: 2025/12/photo.jpg).
  • Опціональні метадані вкладення у _wp_attachment_metadata (розміри, виміри, ескізи).

Потім WordPress генерує публічний URL, комбінуючи базовий URL (зазвичай виведений із home/siteurl плюс /wp-content/uploads) з відносним шляхом із _wp_attached_file. Якщо будь-який із цих вхідних елементів неправильний, ви отримаєте валідні записи в БД, які вказують на мертві URL.

Два наслідки, що важливі в продакшні:

  • Файли можуть існувати і при цьому «не відображатися». Неправильний базовий URL дає 404; сітка Бібліотеки медіа виглядає порожньою, бо ескізи ніколи не завантажуються.
  • База даних може бути правильною і при цьому «не відображатися». Якщо вебсервер не може прочитати wp-content/uploads, ви отримаєте 403 і зламані ескізи. WordPress чемно нічого не покаже і чекатиме, доки ви зрозумієте, що проблема на рівні ОС.

Цитата, щоб не сподіватися на диво, коли вам хочеться «просто перезапустити систему»: Hope is not a strategy. —Chris Snook

Короткий жарт #1: Налагодження WordPress — це як детективна робота, тільки підозрюваний завжди DNS і має алібі.

Цікаві факти та контекст (щоб вас не дивувало)

  1. Вкладення WordPress — це записи. Медіа-елементи живуть у wp_posts як post_type=attachment; тому пошкодження бази або фільтрація можуть «стерти» бібліотеку без дотику до диска.
  2. Поле GUID використовується не за призначенням. WordPress спочатку застосовував GUID як унікальний ідентифікатор (подумайте RSS). Багато тем/плагінів трактують його як URL файлу, що перетворює міграції на повільний хаос.
  3. Шлях uploads став налаштовуваним рано — і залишився «липким». Опції upload_path і upload_url_path можуть зберігатися через оновлення й міграції, навіть якщо вони вже не відповідають реальності.
  4. Папки рік/місяць були вибором для організації та продуктивності. Структура за замовчуванням uploads/2025/12/ зменшує блоат директорій; вимкнення її може створити мільйони файлів в одній директорії й зробити файлові системи примхливими.
  5. Відновлення ескізів стало «річчю», бо метадані старіють. Зміна теми, розмірів або обробки зображень може зробити _wp_attachment_metadata несумісним з тим, що на диску.
  6. Плагіни відвантаження змінили домен відмов. Коли ви переписуєте URL на S3/CDN, ваш WordPress може бути здоровим, а бакет тихо відмовляти в доступі.
  7. Помилки admin-ajax і REST можуть виглядати як «порожня бібліотека». Сітка завантажує дані через JS-виклики; CSP, змішаний контент або заблоковані кінцеві точки можуть спричинити порожній UI без проблем у БД.
  8. CDN може кешувати ваші помилки. Короткий період неправильних URL може зберегтися як 404/403 у кеші, і тоді «виправлення WordPress» не поверне досвід користувача, поки ви не очистите кеш.

Реальні режими відмов: URL у БД, шляхи, переписування, сховище

1) Неправильний URL сайту (або різні версії)

Класика: ви мігрували зі staging у production, переключили HTTPS, додали www або перемістили за зворотним проксі. WordPress зберігає URL у БД, але також виводить їх за виконання. Якщо home і siteurl не відповідають реальності, користувачі бачать змішаний контент, неправильні медіа-URL і адмін-екрани поводяться як автомати з напоями у привидному режимі.

Як це виглядає:

  • Бібліотека медіа завантажується, але ескізи порожні (у мережі видно 404/301 цикли/блокування через змішаний контент).
  • Клік по вкладенню показує зламану іконку зображення.
  • Консоль браузера показує заблоковані запити, помилки CORS або нескінченні редиректи.

2) Перевизначення upload_path / upload_url_path отруюють вас

Більшість сайтів не встановлює ці параметри. Ті, хто встановлює, часто забувають про це. Після міграції WordPress може і далі думати, що uploads знаходяться в /var/www/oldsite/uploads або що публічні URL починаються з якогось застарілого домену. Файли можуть фізично бути в wp-content/uploads, але WordPress шукає їх в іншому місці.

3) Записи в БД присутні, але файли відсутні (або навпаки)

Бекапи й відновлення добре відновлюють лише одну сторону цієї пари. Люди відновлюють базу, але не wp-content/uploads. Або синхронізують uploads, але не БД. Від цього Бібліотека медіа стає музеєм зламаних посилань або купою нефункціональних файлів, за зберігання яких ви платите.

4) Роутинг вебсерверу блокує /wp-content/uploads

Занадто агресивні правила Nginx, Apache .htaccess з deny, плагін безпеки, що повертає 403, або правило WAF, яке вважає JPG підозрілими. Якщо ви не можете отримати відомий файл за допомогою curl, це не проблема WordPress. Це проблема доставки.

5) Права, власність, SELinux/AppArmor

Uploads читаються root, але не користувачем вебсерверу. Або читаються, але перетин директорій блокується через біт виконання. Або SELinux маркує так, що httpd не має доступу. Ці проблеми проявляються як 403 (іноді 404, якщо сервер приховує відмови авторизації).

6) Неправильне налаштування offload / CDN / об’єктного сховища

Плагіни відвантаження або:

  • переписують URL на льоту (тому БД «виглядає нормально», але запити йдуть на S3/CDN), або
  • зберігають cloud-URL у postmeta / GUID / кастомних таблицях.

Поширена відмова: облікові дані були замінені, політика бакету змінена, змінено endpoint регіону, або об’єкти приватні, але подаються без підписаних URL. Адмін WordPress нічого не показує, бо ескізи запитуються з CDN і помилково повертають помилки.

7) Відмовляє UI Бібліотеки медіа, а не медіа

Якщо запит списку вкладень працює, але JavaScript-запити падають, сітка може виглядати порожньою. Причини: CSP заголовки, заблокований admin-ajax.php, проблеми автентифікації REST API, або плагін, що викликає фатальну помилку лише на екрані медіа.

Короткий жарт #2: Бібліотека медіа не зникла; вона просто переїхала на новий домен і не попередила фінансовий відділ.

Практичні завдання: команди, виводи та рішення

Ось завдання, які я насправді виконую, коли хтось у чаті повідомляє «бібліотека медіа порожня». Кожне містить (1) команду, (2) приклад виводу і (3) яке рішення з цього випливає.

Завдання 1: Порахувати вкладення в базі

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

Що це означає: Вкладення існують у БД. Якщо UI порожній, ймовірно, йдеться про генерацію URL, запити ескізів, відмови адмін JS або фільтрацію запитів.

Рішення: Ще не відновлюйте бекапи. Пройдіть перевірку URL/шляхів і перевірку в браузері/мережі.

Завдання 2: Отримати мета для зразкового вкладення (шлях файлу)

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

Що це означає: WordPress очікує файл у uploads/2025/12/hero-banner.jpg відносно бази uploads.

Рішення: Перевірте, чи файл існує на диску або у сховищі offload, і підтвердіть публічний URL, який для нього будується.

Завдання 3: Перевірити опції home та siteurl

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

Що це означає: Розбіжність. Іноді це навмисно (WordPress у підкаталозі), частіше випадково. Це може зламати адмін-виклики та генерацію медіа-URL.

Рішення: Визначте канонічний домен (з www або без, з https). Вирівняйте ці значення або реалізуйте правильні проксі-заголовки й правила перезапису, якщо розділення навмисне.

Завдання 4: Перевірити перевизначення uploads у 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

Що це означає: WordPress явно налаштований на використання застарілого місця зберігання й URL. Ось ваша «порожня бібліотека».

Рішення: Приберіть ці перевизначення (зробіть порожніми) або оновіть їх до правильного шляху/URL. Не робіть цього навмання — спочатку підтвердіть реальне місце зберігання медіа.

Завдання 5: Визначити директорію uploads через конфіг WordPress (WP-CLI)

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

Що це означає: Підтверджує опцію БД з Завдання 4, використовуючи сам WordPress (корисно, коли префікси таблиць відрізняються).

Рішення: Якщо upload_path неправильний, виправте його і перед масовими діями протестуйте отримання одного зображення.

Завдання 6: Перевірити наявність відомого файлу на диску

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

Що це означає: Файл існує, читабельний, належить користувачу вебсерверу (або принаймні групі з читанням). Добре.

Рішення: Якщо файл існує, але медіа порожнє — перевіряйте HTTP-фетчі та правила вебсерверу. Якщо його немає — відновлюйте uploads або погоджуйтеся зі сховищем offload.

Завдання 7: Перевірити права по дереву директорій (ловушка «execute bit»)

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

Що це означає: Директорія uploads має drwxr-x--- і власник root, тому www-data може не мати права переходу. Це майбутній 403.

Рішення: Виправте власність/права на uploads, щоб вебсервер міг переходити і читати. Не ставте chmod 777; це як професійно зустріти рансомваре.

Завдання 8: Якщо SELinux є, перевірте режим та останні відмови

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

Що це означає: SELinux блокує читання, бо контекст файлу неправильний (default_t замість веб-доступного типу).

Рішення: Перемаркуйте uploads з правильним контекстом і зробіть це стійким. Інакше ви будете «лагодити» до наступного відновлення.

Завдання 9: Пропробуйте URL медіа через HTTP (обійдіть браузер)

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

Що це означає: 403 на крайовому рівні. Це не «WordPress забув зображення». Це політика, авторизація, WAF, права або правило, що забороняє /wp-content.

Рішення: Перевірте конфіг Nginx/Apache і шари безпеки. Не чіпайте БД, поки HTTP не віддає відомі файли.

Завдання 10: Перевірити конфіг Nginx на випадкові заборони для 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; }

Що це означає: Хтось заблокував uploads. Часто це робиться, щоб заборонити виконання PHP, але реалізовано як «deny all» замість «deny PHP».

Рішення: Змініть правила так, щоб дозволяти статичні файли і забороняти виконуваний вміст. Повторно протестуйте через curl -I.

Завдання 11: Перевірити Apache .htaccess або vhost правила, що впливають 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]

Що це означає: Правило перезапису забороняє доступ до uploads (повертає 403). Це або непорозуміння, або шматок копіпасти безпекового снипету, застосований занадто широко.

Рішення: Виправте правило, так щоб блокувати тільки небезпечні патерни (наприклад PHP у uploads), а не всю директорію.

Завдання 12: Перевірити, чи запити перенаправляються на неправильний хост

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

Що це означає: Канонічний хост інший. Якщо WordPress думає «www», але ваш редирект примушує non-www (або навпаки), ви можете отримати змішаний контент або заблоковані адмін-виклики залежно від того, як працюють cookie і CORS.

Рішення: Оберіть один канонічний хост і вирівняйте налаштування WordPress, редиректи й конфіг CDN під нього.

Завдання 13: Підтвердити патерни GUID вкладень (виявити застарілі домени)

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

Що це означає: Кожне вкладення ще має у GUID застарілий домен. Це може бути важливим або ні, залежно від поведінки тем/плагінів.

Рішення: Якщо front-end або плагін використовує GUID для URL, потрібне контрольоване оновлення. Якщо ні — можна оновити для послідовності, але робіть це обережно і тестуйте фіди/інтеграції.

Завдання 14: Пошук старих доменів у postmeta (перевизначення URL файлу)

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

Що це означає: Метадані offload посилаються на застарілий домен/налаштування бакета. Це часто реальна причина зламаних ескізів при використанні offload-плагінів.

Рішення: Спочатку виправте налаштування плагіна offload; потім розгляньте інструменти міграції/реанімації метаданих, специфічні для цього плагіна.

Завдання 15: Підтвердити, що WP-CLI бачить вкладення (обійти адмін 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... |
+------+------------------+--------------------------------------------------------------+

Що це означає: WordPress бачить вкладення на рівні додатку.

Рішення: Якщо WP-CLI показує вкладення, але UI порожній — фокусуйтеся на адмін JS, API-викликах і помилках при завантаженні ескізів, а не на «відсутніх рядках у БД».

Завдання 16: Швидко перевірити admin Ajax/REST кінцеві точки (з сервера)

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

Що це означає: Кінцева точка доступна. 302 на логін нормальний для неавторизованих запитів. Якщо ви бачите 403/500 тут, UI Бібліотеки медіа може ламатися.

Рішення: Якщо адмін кінцеві точки падають — перевірте WAF правила, mod_security, кешуючі шари і помилки PHP, перш ніж чіпати медіа-URL.

Завдання 17: Знайти PHP-помилки, пов’язані з запитами медіа (швидкий 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

Що це означає: Відсутня функція бібліотеки зображень (тут підтримка WebP) може ламати генерацію ескізів або операції з метаданими, іноді викликаючи дивну поведінку UI.

Рішення: Встановіть/увімкніть потрібні розширення PHP або вимкніть функцію/плагін, що на неї покладається; потім за потреби регенеруйте ескізи.

Завдання 18: Контрольований пошук/заміну в БД для зміни домену (dry run)

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

Що це означає: WP-CLI не знайшов збігів (або ви не скануєте потрібні таблиці/префікс). Альтернатива: старий домен збережено без схеми або у серіалізованих масивах в іншій формі.

Рішення: Розширте діапазон пошуку (http vs https, з www/без), або націльтесь на конкретні таблиці. Коли вже робитимете реальну заміну — зробіть знімок БД спочатку.

Завдання 19: Перевірити патерни змішаного контенту (http медіа на https сайті)

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

Що це означає: Вкладення ще посилаються на http. Браузери часто блокують або попереджають про це, і адмін-ескізи можуть падати залежно від політик.

Рішення: Підготуйте обережну міграцію на https або забезпечте, щоб WordPress генерував https незалежно від GUID.

Три корпоративні міні-історії з полігону

Міні-історія 1: Інцидент через неправильне припущення

Середня організація мігрувала WordPress з legacy VM у контейнери. План міграції виглядав чистим: дамп/відновлення бази, rsync wp-content, оновлення DNS і готово. Після cutover Бібліотека медіа стала порожньою.

Команда припустила «порожня бібліотека = відсутні uploads». Тому вони повторно запустили rsync, двічі, в робочий час. Потім почали відновлювати старі бекапи, бо ж остання копія ніби пошкоджена. Вони спалили день і створили гарний беклог із неконсистентними станами: деякі сторінки посилалися на медіа, яке існувало лише в першому відновленні, деякі — у другому.

Справжня причина була буденною: upload_url_path у wp_options все ще вказував на старий домен. WordPress генерував URL ескізів на хост, який тепер повертав 301 на внутрішню адресу. З офісного VPN це «працювало», з інтернету — ні. Адмін UI виглядав порожнім, бо кожен запит ескізу редиректився в місце, яке браузер не хотів виконувати через змішані cookie і блокування крос-домену.

Виправлення було антиклімактичним: очистити опцію, вирівняти home/siteurl, очистити CDN — і бібліотека «з’явилась». Файли весь час були на місці. Аутейдж виник через неправильне припущення: що Бібліотека медіа — це прямий перегляд файлової системи. Це не так.

Міні-історія 2: Оптимізація, що повернулась бумерангом

Маркетинг скаржився, що завантаження зображень «повільні», тож інженер додав плагін для відвантаження у об’єктне сховище і CDN попереду. На папері: менше записів на диск, менше навантаження на origin, швидше завантаження сторінок. На практиці: нова продуктова залежність з дуже цікавою площею ураження.

Через кілька тижнів Бібліотека медіа почала показувати порожні квадрати. Не іконки помилок — просто порожньо, ніби зображень ніколи не існувало. Фронтенд також втратив зображення, але не послідовно. Деякі браузери працювали. Інші — ні. Проблема була інтермітентною, що є способом всесвіту посміятись над вами.

Корінь: CDN закешував 403 від об’єктного сховища для підмножини ключів. Оновлення політики тимчасово заборонило читання об’єктів без певного префікса. Плагін продовжував генерувати CDN-URL, а WordPress нічого не підозрював. Ескізи в адміні брались із CDN, який вірно повертав закешовані 403.

«Оптимізація» перемістила доступність медіа з рівня «чи origin може віддати файли?» на «чи політика CDN правильна, чи політика бакету правильна, чи дійсні облікові дані, чи плагін переписує URL правильно, і чи ми очистили кеш після змін політики?» Виправили, скоригувавши політику і прибравши кеш CDN, але справжній урок був у керуванні: ставте відвантаження медіа як платформну зміну, а не швидку установку плагіна.

Міні-історія 3: Нудна, але правильна практика, що врятувала день

Інша компанія мала ритуал: кожна міграція вимагала дві перевірки перед оголошенням успіху. По-перше, підтвердити кількість вкладень і вибірково перевірити записи _wp_attached_file. По-друге, отримати десять випадкових зображень через curl -I з зовні мережі (реальний зовнішній пробник, а не «працює на моєму ноуті»). Ніхто не любив цей чеклист. Ось чому він працював.

Під час переміщення дата-центру Бібліотека медіа виглядала порожньою для частини користувачів. Інженери не панікували. Вони виконали рутинні перевірки. Кількість у БД була в нормі. Файли на диску існували. HTTP-фетчі ззовні повертали 403 — але лише для запитів без певного заголовка. Це звузило пошук до крайнього шару.

Виявилось, що новий профіль WAF блокував запити до /wp-content/uploads/, якщо в них не було браузероподібного User-Agent. Постачальник WAF називав це «захист від ботів». Компанія назвала це «порушенням роботи сайту».

Через те, що вони мали звичку виконувати одні й ті самі тести щоразу, у них були чисті докази: «origin читає файли, WordPress генерує правильний шлях, край повертає 403». Вони швидко виправили правило і уникли класичної помилки — перезапису URL у базі в погоні за проблемою, яка не в базі.

Поширені помилки: симптом → корінна причина → виправлення

  • Симптом: Сітка Бібліотеки медіа порожня; в UI у лічильнику вкладень показує нуль.

    Корінна причина: Адмін JS-запити падають (REST/Ajax блокуються WAF, CSP, плагіном кешування або фатальною помилкою на екрані медіа).

    Виправлення: Перевірте devtools браузера мережу/консоль, потім перевірте admin-ajax.php і REST-ендпоінти. Виправте правило сервера/WAF або конфлікт плагінів перед тим, як чіпати шляхи uploads.

  • Симптом: Вкладення є в БД, але ескізи зламані і повертають 404.

    Корінна причина: Неправильні home/siteurl, або старий домен запечений у перевизначеннях URL.

    Виправлення: Вирівняйте home і siteurl до канонічного домену. Очистіть або виправте upload_url_path. Очистіть кеш CDN після.

  • Симптом: Ескізи повертають 403 з /wp-content/uploads.

    Корінна причина: Правила вебсерверу забороняють доступ, проблеми з обходом директорій або контекст SELinux блокує читання.

    Виправлення: Відрегулюйте правила Nginx/Apache, щоб дозволяти статичні файли; виправте власника/права; перемаркуйте контекст SELinux.

  • Симптом: Файли на диску є, але WordPress зберігає нові медіа в несподіваній директорії.

    Корінна причина: Перевизначення upload_path вказує інше місце, або неправильне маунтування тома в контейнері.

    Виправлення: Видаліть перевизначення, щоб WordPress використовував wp-content/uploads, або виправте шлях маунта. Підтвердіть тестовим завантаженням.

  • Симптом: Медіа видно в адмінці, але зображення зламані лише на фронтенді.

    Корінна причина: CDN кешує старі 404, проблеми зі змішаним контентом http/https, або тема будує URL з GUID інакше ніж адмін.

    Виправлення: Очистіть CDN, застосуйте https і перевірте тему/плагіни на використання GUID. Виправте генерацію канонічних URL.

  • Симптом: Після міграції деякі зображення працюють, нові — ні.

    Корінна причина: Часткове відновлення директорії uploads; відсутні піддиректорії рік/місяць; або синхронізація об’єктного сховища неповна.

    Виправлення: Порівняйте дерева файлової системи, відновіть відсутні директорії або перезапустіть синхронізацію об’єктного сховища з перевіркою.

  • Симптом: Клік по вкладенню показує метадані, але немає прев’ю; зміна розміру не вдається.

    Корінна причина: Відсутні GD/Imagick функції або фатальні помилки PHP при обробці медіа.

    Виправлення: Встановіть/увімкніть потрібні розширення PHP і бібліотеки; потім, якщо потрібне, регенеруйте ескізи, якщо метадані застаріли.

  • Симптом: Бібліотека медіа показує елементи, але пошук/фільтрація нічого не повертає.

    Корінна причина: Плагін змінює запити (хуки) або проблеми з кодуванням/індексами таблиць БД, що спричиняють повільні/невірні запити.

    Виправлення: Вимкніть підозрілі плагіни, перевірте MySQL slow logs і помилки, відремонтуйте таблиці/індекси за потреби.

Чеклісти / покроковий план

Чекліст A: «Бібліотека медіа порожня» у продакшні

  1. Підтвердіть, що в БД є вкладення. Якщо нуль — у вас втрата даних або неправильне з’єднання з базою/префікс таблиць.
  2. Перевірте _wp_attached_file одного вкладення. Переконайтесь, що це адекватний шлях (відносний, а не якийсь дивний абсолютний застарілий шлях, якщо це не навмисно).
  3. Підтвердіть home та siteurl. Визначте свій канонічний домен і схему.
  4. Перевірте upload_path і upload_url_path. Очистіть застарілі перевизначення, якщо вони не навмисні.
  5. Отримайте один відомий медіафайл через curl -I. Зауважте 200 vs 301 vs 403 vs 404.
  6. Перевірте існування файлу на файловій системі та права обходу. Використовуйте namei -l для всього шляху.
  7. Перевірте SELinux/AppArmor, якщо застосовно. Режим Enforcing плюс AVC відмови = безнадійна мука.
  8. Перевірте правила вебсерверу. Блокуйте виконання PHP у uploads, а не всю директорію.
  9. Перевірте CDN/WAF. Шукайте кешовані 403/404 відповіді і блоки по шляху.
  10. Лише потім робіть пошук/заміну в БД. Спочатку зробіть снапшот, робіть dry-run і використовуйте WP-CLI для безпечної обробки серіалізованих даних.

Чекліст B: Безпечна корекція URL/шляхів без погіршення ситуації

  1. Резервна копія бази. Логічний дамп і снапшот якщо доступно. Якщо ви не зможете відкотитися — не виправляйте.
  2. Визначте канонічний URL. https vs http, www vs без www, і чи WordPress у підкаталозі.
  3. Спочатку виправте home/siteurl. Це впливає на багато речей.
  4. Очистіть перевизначення uploads, якщо вони не потрібні. Дозвольте WordPress використовувати wp-content/uploads, якщо немає вагомої причини інакше.
  5. Підтвердіть одне вкладення наскрізь. Запис у БД → файл на диску/в об’єкті → HTTP 200.
  6. Запустіть wp search-replace з --dry-run. Підтвердіть область дії і вибір таблиць.
  7. Реальну заміну робіть у неробочий час. Оцініть тривалість, вплив локування й підготуйте відкат.
  8. Очистіть кеші CDN. Інакше ваше «виправлення» правильне, але невидиме.
  9. Регенеруйте ескізи, якщо змінились розміри. Тільки якщо оригінали доступні.

Чекліст C: Зміцнення, щоб це не повторилось

  1. Моніторьте HTTP для зразкових медіа-об’єктів. Синтетична перевірка, що фетчить кілька відомих uploads, виявить 403/404 рано.
  2. Відстежуйте кількість вкладень і розмір дерева uploads. Великі розбіжності вказують на часткові відновлення або зламані пайплайни.
  3. Документуйте, чи використовуєте offload/CDN. Розглядайте це як критичну інфраструктуру з процедурою змін.
  4. Стандартизуйте обробку канонічного домену. Проксі-заголовки, редиректи і налаштування WordPress повинні узгоджуватись.
  5. Забороніть ad-hoc SQL search/replace у проді. Використовуйте WP-CLI для безпеки серіалізації й аудитованості.

FAQ

Чому Бібліотека медіа порожня, а пости все ще показують зображення?

Часто фронтенд рендерить кешований HTML або використовує інше джерело URL (тема хардкодить, CDN), тоді як адмін-решітка покладається на JS-виклики і кінцеві точки ескізів, які можуть бути заблоковані.

Чи безпечно оновлювати home і siteurl безпосередньо в базі?

Так, якщо ви знаєте правильний канонічний URL і маєте план відкату. Робіть це усвідомлено; невідповідності можуть зламати cookie логіну і доступ в адмінку. Віддавайте перевагу WP-CLI коли можливо для послідовної поведінки.

У чому різниця між home і siteurl?

home — це публічна адреса сайту. siteurl — це місце, де лежать файли ядра WordPress. Зазвичай вони однакові, окрім випадків інсталяцій у підкаталозі або нестандартних налаштувань. Випадкові відмінності викликають дивну поведінку.

Чи варто «виправляти» GUID вкладень під час міграції?

Іноді. WordPress не покладається на GUID для медіа-URL в чистому потоці, але плагіни і теми можуть. Якщо ви бачите, що GUID використовуються в шаблонах або інтеграціях — плануйте оновлення обережно.

Чому я отримую 403 при запиті файлів у wp-content/uploads?

Або конфіг вебсерверу забороняє шлях, або файлові права блокують обхід/читання, або SELinux/AppArmor забороняє доступ. Не припускайте, що це баг WordPress; доведіть це через curl -I і логи.

Після виправлення URL чому все ще відсутні ескізи?

CDN і браузери кешують помилки. Також ескізи можуть бути відсутні на диску, якщо ви відновили лише оригінали або змінили розміри зображень. Очистіть кеші, а потім за потреби регенеруйте ескізи, якщо оригінали доступні.

Як визначити, чи задіяний плагін offload?

Зверніть увагу на налаштування плагіна пов’язані з S3/об’єктним сховищем, перевірте postmeta на ключі як amazonS3_info, і подивіться мережеву панель браузера — якщо URL вказують на CDN/бакет, ви відвантажуєте медіа.

Чи може зворотний проксі викликати «порожню бібліотеку медіа»?

Так. Якщо WordPress не бачить правильну схему/хост (відсутні проксі-заголовки), він може генерувати http-URL на сайті з https, що призводить до блокування через змішаний контент або редирект-циклів та зупиняє завантаження ескізів.

Яка найчастіша помилка при міграції?

Відновлення бази без відновлення wp-content/uploads (або навпаки відновлення uploads без бази). Медіа — це пов’язана система: метадані + байти.

Висновок: наступні кроки, які реально знижують ризик

Якщо ваша Бібліотека медіа WordPress виглядає порожньою, вгамуйте бажання «перевстановити WordPress» або запускати випадкові search/replace прямо в проді. Виправлення зазвичай полягає у невідповідності між тим, що WordPress вважає за URL/шлях uploads, і тим, що ваша інфраструктура фактично подає.

Зробіть це далі:

  1. Доведіть, що вкладення існують у БД і дістаньте зразок _wp_attached_file.
  2. Вирівняйте home/siteurl і прибирайте або коригуйте upload_path/upload_url_path.
  3. Отримайте відомий upload через curl -I і лагодьте край/вебсервер/права, доки не отримаєте чистий 200.
  4. Якщо відвантаження задіяне — перевірте права бакету/CDN і очистьте закешовані 403/404.
  5. Лише потім запускайте контрольоване search/replace через WP-CLI і (за потреби) регенеруйте ескізи.

Зробіть це нудним: додайте невелику синтетичну перевірку, яка фетчитиме кілька відомих uploads. Найкращий час виявити, що ваші uploads заблоковані — ніколи.

← Попередня
Моніторинг MySQL і Percona Server: знаходьте критичні запити без здогадок
Наступна →
Виправлення Proxmox PBS «Authentication Failed»: токени, права й відбитки

Залишити коментар