WordPress «Папка призначення вже існує»: виправити встановлення без шкоди wp-content

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

Зараз 09:12, маркетинг вимагає новий плагін форми «прямо зараз», а WordPress спокійно відповідає: «Папка призначення вже існує.» Сайт все ще завантажується, але адмінка застрягла, встановлення не проходять, і кожний «застосунок» з інтернету радить видалити щось під wp-content, ніби це серветка.

Не робіть цього. wp-content — це місце, де ваш бізнес зберігає свої черевики. Якщо почнете тягати їх туди-сюди, решту дня будете вибачатися перед людьми з календарями.

Що насправді означає ця помилка (і чого вона не означає)

WordPress викидає «Папка призначення вже існує», коли намагається встановити або оновити плагін/тему і виявляє, що цільовий каталог з таким ім’ям уже присутній. Звучить очевидно, але ключова деталь така: WordPress не може з упевненістю визначити, чи існуючий каталог — це робоча інсталяція, часткова розпаковка, застарілий залишок після невдалого оновлення, чи щось, до чого він не повинен торкатися.

Тому WordPress грає змовчанням і відступає. Зазвичай це правильний вибір. Ваше завдання — визначити, з чим ви маєте справу:

  • Легітимний існуючий плагін/тема (ви намагаєтесь «встановити» те, що вже є).
  • Часткова інсталяція (каталог є, але файлів бракує; WordPress відмовляється перезаписувати).
  • Проблеми з правами/власністю (WordPress не може видалити/замінити каталог, тож повідомляє, що він «існує», навіть якщо потрібно замінити).
  • Невідповідність структури ZIP (архів розпаковується в ім’я папки, що конфліктує з наявною).
  • Невідповідність абстракцій файлової системи (прямі записи vs FTP/SSH методи, тимчасові каталоги і ігри з umask).

Що це зазвичай не означає:

  • «WordPress зламався» і потрібно перевстановити весь сайт.
  • Причина видаляти випадкові папки під wp-content, поки екран не зміниться.
  • Причина змінювати права на все до 777 (якщо тільки ваша модель загроз не «я люблю жити ризиковано»).

Цитата, варта стіни біля терміналу: «Надія — це не стратегія.» — Ген. Гордон Р. Салліван. Помилки WordPress часто спонукають до операцій надією. Не слідуйте їм.

Швидкий план діагностики (перевірте це передусім)

Якщо ви обмежені в часі, не блукайте. Робіть це як триаж інциденту.

Перш за все: підтвердьте, що саме WordPress намагається встановити

  • Плагін чи тема? Ім’я/slug? З репозиторію чи завантажено ZIP?
  • Це встановлення, оновлення чи спроба «повторної інсталяції»?
  • Чи попереднє оновлення зазнало невдачі (білий екран, «режим обслуговування», застрягле повідомлення про оновлення)?

По-друге: подивіться каталог призначення

  • Чи існує wp-content/plugins/<slug> або wp-content/themes/<slug>?
  • Це повне дерево чи напіврозпакований безлад?
  • Чи є дивні сусідні об’єкти на кшталт <slug>.tmp, <slug>_old або залишок upgrade?

По-третє: перевірте власність, права та шлях запису

  • Чи може користувач веб-сервера записувати в wp-content і wp-content/upgrade?
  • Чи WordPress використовує прямий доступ до файлової системи або запитує FTP-облікові дані?
  • Чи заповнений диск або вичерпані іноди?

По-четверте: перевірте логи (так, дійсно)

  • Лог помилок веб-сервера: відмови в дозволах, помилки розпаковки, таймаути.
  • Лог PHP-FPM: обмеження open_basedir, проблеми з тимчасовою директорією.
  • Дебаг-лог WordPress: повідомлення про файлову систему, оновлення та розпаковку.

По-п’яте: вирішіть найменш ризиковий шлях

  • Якщо каталог містить дійсну інсталяцію: робіть оновлення, а не встановлення; або використайте WP-CLI з --force обережно.
  • Якщо це часткова інсталяція: перейменуйте її, а потім встановіть чисто.
  • Якщо це проблема з правами: виправте власність/ACL, потім повторіть оновлення.
  • Якщо це структура ZIP: перепакуйте або виберіть інший ZIP.

Цікаві факти та історичний контекст (щоб дивні речі мали сенс)

Шість–десять коротких фактів, що пояснюють, чому ця помилка існує і чому вона досі дратує:

  1. WordPress спочатку розраховував на shared hosting, де «встановлення плагіна» означало «завантаження через FTP», а не атомарну заміну директорій.
  2. Система оновлення покладається на тимчасовий робочий простір (зазвичай wp-content/upgrade) і послідовність копіювання/перейменування; коли ця послідовність переривається, залишки — часте явище.
  3. ZIP-архіви не відзначаються стандартизованою добротою: багато вендорів упаковують ZIP з верхньою папкою, яка не відповідає slug плагіна, що спричиняє колізії або вкладені папки.
  4. На деяких хостах PHP працює під іншим користувачем, ніж вебсервер (або під пуловим користувачем), створюючи змішану власність, яка дає збої лише під час записів.
  5. Логіка «методу файлової системи» існує тому, що WordPress не може припустити доступ на запис; він пробує прямі записи, потім відкочується до FTP/SSH методів, коли блокується.
  6. Атомарна заміна директорій не завжди доступна на всіх файлових системах/моделях прав; WordPress діє обережно, щоб не видалити робочий код.
  7. Каталоги плагінів і тем стали майже пакетом менеджером задовго до того, як WordPress отримав надійну семантику відкатів/транзакцій.
  8. Застряглий «режим обслуговування» походить від одного файлу (.maintenance), створеного під час оновлень; якщо очищення не відбулося, сайт може виглядати «вниз», хоча насправді все в порядку.

Сценарії відмов: реальні причини, чому папка «вже існує»

1) Плагін/тема вже встановлені, але ви знову «встановлюєте»

Найчастіше трапляється з завантаженням ZIP і пакунками вендорів. Папка існує, WordPress бачить її, відмовляється перезаписувати і виводить різке повідомлення. Правильний крок — або оновити з сторінки Плагінів, або видалити/перейменувати існуючий каталог після перевірки, що це безпечно.

2) Часткова розпаковка через попередню помилку

Таймаут під час розпаковки, заповнений диск, убитий PHP-воркер, таймаут зворотного проксі або інженер, який перезапустив PHP-FPM «щоб почистити». В результаті отримуєте каталог, який є, але неповний. WordPress не може вирішити, чи його видаляти, тому зупиняється.

3) Невідповідність прав/власності

Каталог існує і його треба замінити, але веб-процес не може його видалити. WordPress повідомляє «вже існує», бо спроба очищення зазнала невдачі і продовжувати небезпечно. Шукайтe Permission denied в логах — там і правда буде підказка.

4) SELinux/AppArmor або жорсткі політики

На захищених системах права можуть виглядати правильно, але обов’язковий контроль доступу блокує записи. Це проявляється як помилки доступу навіть при «правильних» бітових режимах.

5) Проблеми open_basedir або тимчасової директорії

Розпаковка часто використовує тимчасову директорію; якщо PHP не може записати туди, отримуєте напівзавершені встановлення і заплутані помилки. WordPress тоді зіштовхується з частково створеною папкою призначення і зупиняється.

6) Дивна поведінка файлової системи (NFS, CIFS, об’єктні томи)

Мережеві файлові системи можуть мати кешування, затримку видимості або семантику перейменування, що відрізняється від локальних ext4/XFS. Заміна директорії може провалюватися так, що виглядає як «існує», але насправді означає «перейменування не поширилося».

7) Інструменти деплойменту, що конфліктують з WordPress

Якщо ви деплоїте код через Git, rsync або контейнери, і WordPress намагається самовносити зміни, у вас два джерела істини, що змагаються. Помилка — це WordPress, який гавкає на зачинені двері.

Жарт №1: Оновлення WordPress — як офісні крісла: зазвичай все нормально, доки хтось не відкидається з упевненістю.

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

Це реальні завдання, які ви можете виконати на Linux-хості. Кожне включає: команду, що типовий вивід означає, і як ви вирішуєте.

Завдання 1: Підтвердити наявність цільового каталогу

cr0x@server:~$ sudo ls -ld /var/www/html/wp-content/plugins/contact-form-7
drwxr-xr-x 5 root root 4096 Dec 27 08:41 /var/www/html/wp-content/plugins/contact-form-7

Значення: Каталог плагіна існує і належить root:root. Це підозріло на типовому WordPress-сервері, де веб-процес потребує прав на запис.

Рішення: Поки не видаляйте. Перевірте, чи це дійсна інсталяція і чи неправильно вказана власність.

Завдання 2: Перевірити, чи це часткова інсталяція (кількість файлів і очікувані файли)

cr0x@server:~$ sudo find /var/www/html/wp-content/plugins/contact-form-7 -maxdepth 2 -type f | wc -l
3

Значення: Лише 3 файли — це ймовірно неповна інсталяція для справжнього плагіна. Здоровий плагін зазвичай має десятки файлів.

Рішення: Розглядайте як часткову розпаковку. Плануйте перейменувати і перевстановити.

Завдання 3: Виявити користувача веб-сервера (Apache)

cr0x@server:~$ ps -eo user,comm | egrep 'apache2|httpd' | head
www-data apache2
www-data apache2
www-data apache2

Значення: Процеси Apache працюють як www-data.

Рішення: Каталоги плагінів/тем зазвичай повинні бути записувані www-data (або керуватися через деплоймент, але тоді WordPress не повинен оновлюватися самостійно).

Завдання 4: Виявити користувача пулу PHP-FPM (Nginx + PHP-FPM)

cr0x@server:~$ sudo grep -R "^\s*user\s*=" /etc/php/*/fpm/pool.d/www.conf | head -n 1
user = www-data

Значення: PHP виконується як www-data також (добре; менше несподіванок з мішаною власністю).

Рішення: Якщо ви все ще маєте каталоги, що належать root, вони ймовірно створені вручну (розпаковка як root, rsync як root або крок CI, що пішов не так).

Завдання 5: Перевірити записуваність wp-content від імені веб-користувача

cr0x@server:~$ sudo -u www-data bash -lc 'touch /var/www/html/wp-content/.write-test && echo ok && rm /var/www/html/wp-content/.write-test'
ok

Значення: Веб-користувач може записувати в wp-content принаймні на верхньому рівні.

Рішення: Помилка ймовірніше в конкретному підкаталозі (plugins/themes/upgrade) або через власність/ACL на тому каталозі.

Завдання 6: Перевірити здоров’я тимчасового каталогу upgrade

cr0x@server:~$ sudo -u www-data bash -lc 'mkdir -p /var/www/html/wp-content/upgrade && touch /var/www/html/wp-content/upgrade/.upgrade-test && ls -la /var/www/html/wp-content/upgrade | head'
total 8
drwxr-xr-x  2 www-data www-data 4096 Dec 27 09:01 .
drwxr-xr-x 10 www-data www-data 4096 Dec 27 09:01 ..
-rw-r--r--  1 www-data www-data    0 Dec 27 09:01 .upgrade-test

Значення: WordPress може підготовлювати файли для оновлення.

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

Завдання 7: Перевірити вільне місце на диску та іноди

cr0x@server:~$ df -h /var/www/html
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        40G   39G  600M  99% /

Значення: Ви фактично без вільного місця. Розпаковка може обірватися посередині, залишивши каталог призначення.

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

cr0x@server:~$ df -i /var/www/html
Filesystem      Inodes  IUsed   IFree IUse% Mounted on
/dev/sda1      2621440 2619000   2440  100% /

Значення: Іноди вичерпані. Це може ламати інсталяції навіть коли «місце на диску» виглядає нормально в інших розділах.

Рішення: Знайдіть і видаліть інодоємний смітник (каталоги кешу, старі резервні копії). Потім повторіть.

Завдання 8: Шукати застряглий режим обслуговування

cr0x@server:~$ sudo ls -la /var/www/html/.maintenance
-rw-r--r-- 1 www-data www-data 57 Dec 27 08:40 /var/www/html/.maintenance

Значення: Попереднє оновлення не прибрало файл. Користувачі можуть бачити «Короткочасно недоступно через планове обслуговування.»

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

Завдання 9: Перевірити debug-лог WordPress на помилки розпаковки/фс

cr0x@server:~$ sudo tail -n 30 /var/www/html/wp-content/debug.log
[27-Dec-2025 08:41:12 UTC] PHP Warning:  copy(/var/www/html/wp-content/plugins/contact-form-7/readme.txt): failed to open stream: Permission denied
[27-Dec-2025 08:41:12 UTC] PHP Warning:  unlink(/var/www/html/wp-content/plugins/contact-form-7/readme.txt): Permission denied

Значення: Класична проблема власності/дозволів всередині каталогу призначення.

Рішення: Виправте власність/режим/ACL на цьому шляху. Не продовжуйте постійно повторювати встановлення; ви просто зловите журнали і нетерплячість.

Завдання 10: Переглянути логи веб-сервера / PHP для реальної помилки

cr0x@server:~$ sudo tail -n 50 /var/log/nginx/error.log
2025/12/27 08:41:12 [error] 12345#12345: *901 FastCGI sent in stderr: "PHP message: PHP Warning:  mkdir(): Permission denied in /var/www/html/wp-admin/includes/class-wp-filesystem-direct.php on line 56" while reading response header from upstream

Значення: Метод файлової системи — direct, але mkdir зазнав відмови. Це не «логічна» проблема WordPress; це проблема на рівні ОС з доступом.

Рішення: Виправте права/власність, потім повторіть. Уникайте хакерських перезавантажень ZIP через адмінку, доки шлях запису не працює правильно.

Завдання 11: Перевірити структуру ZIP перед завантаженням (поширена проблема вендорів)

cr0x@server:~$ unzip -l /tmp/vendor-plugin.zip | head
Archive:  /tmp/vendor-plugin.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2025-12-01 10:10   vendor-plugin/
        0  2025-12-01 10:10   vendor-plugin/vendor-plugin/
     2345  2025-12-01 10:10   vendor-plugin/vendor-plugin/plugin.php

Значення: Вкладена папка: vendor-plugin/vendor-plugin/. WordPress розпаковує до vendor-plugin, а фактичний плагін лежить на рівень глибше, що створює плутанину і може колідувати з наявними директоріями.

Рішення: Перепакуйте ZIP (топ-рівнева папка має бути slug плагіна і містити файли плагіна без зайвих вкладень) або встановіть через WP-CLI з коректного шляху.

Завдання 12: Використати WP-CLI, щоб побачити, що WordPress вважає встановленим

cr0x@server:~$ cd /var/www/html && sudo -u www-data wp plugin list --field=name | head
akismet
contact-form-7
hello

Значення: WordPress вже фіксує contact-form-7 як встановлений. Спроба встановлення з адмінки надлишкова або каталог пошкоджений, але все ще детектується.

Рішення: Розгляньте контрольоване перевстановлення через WP-CLI (з перезаписом), або перейменуйте каталог і встановіть чисто.

Завдання 13: Контрольоване перевстановлення через WP-CLI (плагін) без сліпого видалення

cr0x@server:~$ cd /var/www/html && sudo -u www-data wp plugin install contact-form-7 --force
Installing Contact Form 7 (6.0.3)
Downloading installation package from https://downloads.wordpress.org/plugin/contact-form-7.6.0.3.zip...
Unpacking the package...
Installing the plugin...
Plugin installed successfully.

Значення: --force наказує WP-CLI перезаписати. Це безпечніше, ніж ручне rm -rf, якщо ви довіряєте джерелу і права файлової системи коректні.

Рішення: Якщо це спрацювало, проблема була в адміні, що відмовлявся перезаписати, тоді як WP-CLI зробив це. Якщо не спрацювало, читайте точну помилку і виправляйте причину на рівні ОС.

Завдання 14: Перейменувати каталог призначення, щоб зберегти докази

cr0x@server:~$ sudo mv /var/www/html/wp-content/plugins/contact-form-7 /var/www/html/wp-content/plugins/contact-form-7.broken-20251227

Значення: Ви прибрали колізію і при цьому зберегли старий каталог для судової експертизи або відкату.

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

Завдання 15: Виправити власність коректно (і тільки там, де потрібно)

cr0x@server:~$ sudo chown -R www-data:www-data /var/www/html/wp-content/plugins/contact-form-7

Значення: Веб-користувач тепер може керувати файлами всередині цього каталогу плагіна.

Рішення: Повторіть оновлення/встановлення. Якщо у вас є пайплайн деплойменту, що очікує власності root, зупиніться і вирішіть, хто володіє кодом: WordPress чи ваш пайплайн. Виберіть одне.

Завдання 16: Виявити і зняти незнімні атрибути (рідкісно, але гостро)

cr0x@server:~$ sudo lsattr -d /var/www/html/wp-content/plugins/contact-form-7
----i---------e------- /var/www/html/wp-content/plugins/contact-form-7

Значення: Директорія має атрибут immutable (i). Видалення і записи будуть зазнають невдачі навіть від root.

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

cr0x@server:~$ sudo chattr -i /var/www/html/wp-content/plugins/contact-form-7

Завдання 17: Перевірити контекст SELinux (якщо застосовно)

cr0x@server:~$ sudo getenforce
Enforcing
cr0x@server:~$ sudo ls -Zd /var/www/html/wp-content
unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/wp-content

Значення: Якщо wp-content має мітку httpd_sys_content_t, Apache може читати, але не обов’язково записувати. Записувані шляхи зазвичай потребують httpd_sys_rw_content_t залежно від політики.

Рішення: Налаштуйте контексти лише для записуваних директорій, а не для всього docroot.

Завдання 18: Знайти залишкові робочі каталоги upgrade

cr0x@server:~$ sudo find /var/www/html/wp-content/upgrade -maxdepth 1 -type d -printf '%f\n' | head
upgrade
tmp-1234567890
tmp-1735290072

Значення: Старі тимчасові каталоги можуть вказувати на невдалі оновлення.

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

Жарт №2: Нічого так не виглядає «enterprise-grade», як папка з ім’ям tmp-1735290072, яка тихо контролює ваш вікенд.

Безпечні виправлення, що не ламають wp-content

Стратегія A: Перейменуйте, не видаляйте (за замовчуванням переміщення)

Коли ви не на 100% упевнені, що в тій директорії, перейменуйте її. Перейменування оборотне, швидке і зберігає докази. Єдиний випадок, коли можна видалити одразу — коли ви чистите відому кеш-директорію або відомий тимчасовий каталог.

Добре: contact-form-7.broken-20251227
Погано: rm -rf contact-form-7 і потім «думаю, воно було в порядку?»

Стратегія B: Використовуйте WP-CLI для контрольованого перезапису

Адмінка WordPress обережна. WP-CLI — прямий інструмент. Якщо ви — ops людина, хочете детерміністичну поведінку і журнали, які можна зафіксувати. Використовуйте:

  • wp plugin install <slug> --force
  • wp theme install <slug> --force

Але лише після підтвердження, що шлях запису та права в порядку. Якщо права неправильні, примусові записи просто призведуть до голоснішої невдачі.

Стратегія C: Виправляйте власність/права виважено

Найнебезпечніше «вирішення» у світі WordPress — це масові зміни прав:

  • Уникайте: chmod -R 777 будь-де.
  • Уникайте: рекурсивного chown усього docroot, якщо ви деплоїте код через Git/CI; це створить дрейф і здивує наступний деплой.

Робіть замість цього: тримайте код ядра під власністю користувача деплойменту (або root), а робочі директорії робіть доступними для запису лише для runtime-користувача:

  • wp-content/uploads
  • wp-content/cache (якщо використовується)
  • wp-content/upgrade
  • каталоги плагінів/тем лише якщо ви дозволяєте оновлення на місці

Стратегія D: Забороніть WordPress самовносити зміни в production (якщо можете)

Суб’єктивний погляд: на серйозних production-системах WordPress не повинен мати змогу оновлювати себе клацанням у wp-admin. Це не пуризм; це зменшення неконтрольованих змін. Якщо у вас є CI/CD, immutable-образи або навіть базовий контроль змін, керуйте оновленнями як деплойментами. Тоді цей клас помилок значно зменшиться.

Компроміс: ви мусите також припинити просити WordPress бути пакетним менеджером. Виберіть модель:

  • Модель 1: WordPress керує плагінами/темами. Ви забезпечуєте права і приймаєте ризик змін через адмінку.
  • Модель 2: Ваш пайплайн керує плагінами/темами. Ви вимикаєте модифікації файлів у WordPress і деплоїте артефакти.

Стратегія E: Відновити пошкоджену інсталяцію без втрати налаштувань

Деякі плагіни зберігають налаштування в базі даних, а не у файловій системі. Якщо директорія плагіна пошкоджена, зазвичай можна перевстановити файли плагіна, не втрачаючи налаштувань. Але «зазвичай» не означає «завжди».

Безпечний підхід:

  1. Зробіть резервну копію бази даних (або снапшот).
  2. Резервний копіюйте директорію плагіна, перейменувавши її.
  3. Чисто перевстановіть плагін/тему.
  4. Перевірте поведінку і логи.
  5. Лише потім видаліть старий каталог.

Три корпоративні міні-історії (як це йде не так у реальному житті)

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

Середня компанія запускала WordPress за Nginx і PHP-FPM на віртуалці. Маркетинг мав доступ адміністратора і встановлював плагіни напряму. Оперна команда припустила, що раз сайт працював місяцями, права файлів «в порядку». Насправді ні — просто не тестували.

Одного ранку оновлення плагіна впало під час розпаковки. Каталог плагіна існував, був частково оновлений, і WordPress відмовився перевстановити: «Папка призначення вже існує.» Панель адміністрування постійно пропонувала оновлення, які не могли завершитись. Ніхто не перевірив місце на диску чи власність; просто п’ять разів повторили оновлення, створюючи ще більше тимчасових директорій.

Неправильне припущення було тонким: «якщо WordPress може читати плагіни, він може їх оновити.» На тому хості попереднє техобслуговування скопіювало каталоги плагінів як root з резервного tarball. WordPress міг читати, але не міг їх замінити.

Виправлення було нудним: chown лише на уражений каталог плагіна і каталог підготовки оновлення до PHP-FPM-користувача, потім перевстановлення через WP-CLI. Пост-мортемна дія: заборонити оновлення в продакшні і перемістити управління плагінами в пайплайн деплойменту.

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

Організація перемістила wp-content на мережеву файлову систему, щоб декілька вузлів могли спільно використовувати завантаження і плагіни. Це виглядало акуратно: одне джерело правди, немає rsync медіафайлів, простіше масштабування. Оптимізація була «спільне сховище вирішує все», і такі речення швидко старіють.

Під час оновлення плагіна WordPress розпаковував ZIP на вузлі A, але вузол B ще бачив старий стан через кешування і затримку метаданих. WordPress на вузлі B теж спробував оновити (так, люди іноді натискають кнопки двічі), зіткнувся з директорією в проміжному стані і викинув «Папка призначення вже існує.» Плагін вийшов напів-новим, напів-старим по різних вузлах.

Вони спробували «виправити» це шляхом збільшення таймаутів PHP і додавання повторних спроб. Це збільшило радіус ураження. Зрештою довелося зробити адмінку лише для читання під час деплойментів і забезпечити, щоб лише один вузол виконував оновлення — або ще краще, припинити оновлення через wp-admin зовсім.

Урок не в тому, щоб ніколи не використовувати NFS. Він полягає в тому, що якщо ваш додаток очікує локальної семантики файлової системи для атомарної заміни директорій, потрібно про це думати. Або централізуйте оновлення через одне завдання деплойменту, або пакетуйте плагіни/теми як артефакти і розгортайте передбачувано.

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

Великий внутрішній WordPress-службовий інстанс обслуговував документацію. Це не було гламурно, але критично. Команда дотримувалася двох практик: (1) кожна зміна проходила через пайплайн деплойменту, і (2) файлову систему було майже зроблено лише для читання для runtime-користувача, окрім uploads і невеликого набору записуваних директорій.

Одного дня вендор надіслав «hotfix плагін» у ZIP і наполягав на негайній інсталяції. Адміністратор спробував. WordPress не міг писати в директорію плагінів (задумано), і UI помилка була — передбачувано — варіантом конфлікту папки призначення / неможливістю перезаписати. Паніка тривала близько п’яти хвилин.

Оскільки практика була нудною та послідовною, відповідь була простою: команда підготувала плагін у билд-воркспейсі, перевірила структуру ZIP, перепакувала його правильно, відсканувала, а потім задеплоїла як будь-яку іншу зміну. Ніяких ручних chmod. Ніякої північної археології в wp-content.

Сайт залишився стабільним, а «аварія» стала звичайною зміною з аудитом. Дивно, як часто «ми деплоїмо однаково кожного разу» — це різниця між дрібним незручністю і великим простоєм.

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

1) Симптом: «Папка призначення вже існує» при завантаженні ZIP

Корінна причина: Каталог плагіна/теми вже існує від попередньої інсталяції, або ZIP містить топ-рівневу папку, що співпадає з наявною директорією.

Виправлення: Перевірте шлях призначення. Перейменуйте існуючий каталог. Перевірте макет ZIP за допомогою unzip -l. Перепакуйте, якщо є вкладення.

2) Симптом: Помилка повторюється після того, як ви «видалили плагін» у wp-admin

Корінна причина: Видалення в UI не пройшло через права; каталог залишився на диску. UI WordPress — це не обіцянка, це запит.

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

3) Симптом: Оновлення не вдається; каталог плагіна існує, але плагін зламаний

Корінна причина: Часткова розпаковка через таймаут, заповнений диск, вичерпані іноди або убитий PHP-воркер.

Виправлення: Звільніть диск/іноди, очистіть застарілі тимчасові каталоги upgrade, перейменуйте пошкоджений каталог плагіна, перевстановіть через WP-CLI.

4) Симптом: WordPress несподівано просить FTP-облікові дані

Корінна причина: WordPress не може писати напряму в файлову систему, тому переключається на метод файлової системи через FTP.

Виправлення: Виправте права/власність для runtime-користувача на потрібних директоріях, або оберіть FTP/SSH робочий процес (не рекомендовано для production).

5) Симптом: Права виглядають правильно, але оновлення все одно не проходять

Корінна причина: SELinux/AppArmor блокує записи; або обмеження open_basedir/тимчасових директорій.

Виправлення: Перевірте режим примусового виконання та контексти; перевірте тимчасову директорію PHP; налаштуйте політику тільки для конкретних записуваних шляхів.

6) Симптом: Працює на одному вузлі, на іншому — ні (мультивузлова)

Корінна причина: Проблеми узгодженості спільного сховища або різні runtime-користувачі/UID мапінги між вузлами.

Виправлення: Забезпечте узгоджені UID/GID, централізуйте оновлення, уникайте оновлень через wp-admin у кластерних середовищах.

7) Симптом: Лише один плагін/тема не вдається оновити; інші — нормально

Корінна причина: Саме ця директорія має іншу власність, ACL, атрибут immutable або пошкоджений вміст.

Виправлення: Проінспектуйте цей шлях за допомогою ls -l, getfacl, lsattr. Ремонтуйте локально.

8) Симптом: «Не вдалося створити директорію» під час інсталяції, а потім «папка призначення вже існує» при наступній спробі

Корінна причина: Перша спроба створила директорію, але не змогла її заповнити; друга спроба зіткнулась із пустою залишковою папкою.

Виправлення: Видаліть/перейменуйте пусту папку, виправте первинну причину (права/диск/тимчасова директорія), потім повторіть інсталяцію.

Чеклісти / покроковий план (розумний, повторюваний)

Чекліст 1: Односерверний WordPress, де дозволені встановлення через wp-admin

  1. Визначте slug плагіна/теми, яку встановлюєте (ім’я папки під wp-content).
  2. Перевірте наявність: чи існує вже директорія?
  3. Перевірте стан: повна інсталяція чи часткова?
  4. Перевірте шлях запису: чи може runtime-користувач писати в wp-content і wp-content/upgrade?
  5. Перевірте диск/іноди: не налагоджуйте інсталяції на повній файловій системі.
  6. Перейменуйте директорію, якщо підозрюєте часткову інсталяцію або корупцію.
  7. Перевстановіть через WP-CLI, якщо можливо (це простіше і скриптується).
  8. Перевірте: плагін активується, логи чисті, сайт відображається.
  9. Приберіть стару перейменовану директорію після періоду спостереження.

Чекліст 2: Production-система з CI/CD (рекомендовано)

  1. Вимкніть модифікацію файлів в production (щоб wp-admin не міг мутавати код).
  2. Зробіть docroot доступним лише для читання для runtime-користувача, окрім uploads і контрольованого набору директорій.
  3. Будуйте артефакти (плагіни/теми з зафіксованими версіями) в CI, а не на сервері.
  4. Деплойте атомарно (директорії release + swap через symlink), де можливо.
  5. План відкату повинен існувати і бути протестований (так, протестований).
  6. Спостерігайте: логи помилок, PHP-FPM і дебаг-логи WordPress збираються централізовано.

Крок за кроком: чисте відновлення після «папка призначення вже існує» (плагін)

  1. Зробіть снапшот файлової системи або хоча б швидкий tar каталогу wp-content/plugins/<slug> і дамп бази даних.
  2. Підтвердьте, що директорія існує і огляньте її.
  3. Перейменуйте <slug> в <slug>.broken-<date>.
  4. Забезпечте, щоб runtime-користувач міг писати в wp-content і wp-content/upgrade.
  5. Встановіть/перевстановіть, використовуючи WP-CLI з --force (або встановіть через адмінку, коли колізію видалено).
  6. Активуйте плагін і проведіть швидкий функціональний тест (фронтенд, wp-admin, будь-які cron-завдання).
  7. Слідкуйте за логами протягом 10–15 хвилин нормального використання.
  8. Видаліть перейменовану пошкоджену директорію, коли впевнитесь.

Поширені питання

1) Чи означає «Папка призначення вже існує», що плагін уже встановлений?

Іноді. Це лише означає, що існує директорія з таким ім’ям. Вона може бути здоровою інсталяцією або напіврозпакованою директорією після невдалого оновлення. Підтвердіть за допомогою wp plugin list і огляду вмісту каталогу.

2) Чи варто видаляти папку під wp-content/plugins, щоб виправити це?

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

3) Чому WordPress відмовляється перезаписувати папку?

Тому що перезапис може бути руйнівним, якщо в існуючій директорії є робоча версія або локальні модифікації. WordPress обирає безпеку над зручністю, навіть якщо це незручно.

4) Чи може WP-CLI виправити це швидше, ніж wp-admin?

Так. WP-CLI дає детерміністичні прапори як --force і чіткіший вивід помилок. Також він уникає таймаутів браузера/проксі під час великих розпаковок.

5) Чи знищить перевстановлення плагіна його налаштування?

Зазвичай налаштування плагіна зберігаються в базі даних і переживуть перевстановлення файлів. Але деякі плагіни зберігають конфігуаційні файли в своєму каталозі. Зробіть резервну копію каталогу і бази даних перед перезаписом.

6) Я виправив права, але помилка лишається. Що далі?

Перевірте відмови SELinux/AppArmor, атрибути immutable і вичерпання диску/інодів. Також перевірте структуру ZIP — вкладені директорії можуть викликати повторні колізії.

7) Чому це частіше трапляється в кластерних налаштуваннях?

Тому що спільне сховище і кілька вузлів додають проблеми узгодженості і конкуренції. Два вузли, що одночасно намагаються «оновити» ту саму директорію — типовий сценарій напівнового коду і заплутаних перевірок існування.

8) Чи дозволяти WordPress оновлювати плагіни/теми в production?

Якщо у вас невеликий сайт на одиночному сервері і ви приймаєте ризик, це може бути нормально за наявності хороших резервних копій. Якщо ви працюєте в production з контролем змін, використовуйте CI/CD і обмежте запис runtime.

9) А як щодо налаштування WordPress FS_METHOD?

Примусове встановлення FS_METHOD в direct може приховати глибші проблеми з правами в деяких налаштуваннях і погіршити безпеку в інших. Виправіть базову власність/ACL і дозволяйте WordPress використовувати direct лише коли це доречно.

Наступні кроки, які ви реально можете зробити сьогодні

Помилка «Папка призначення вже існує» — це WordPress, що каже: «Я бачу щось на диску і не впевнений, чи мені на це наступати.» Ставтеся до цього як до запобіжного механізму, а не як до образи.

  1. Програйте швидкий план діагностики: підтвердіть slug, огляньте каталог призначення, перевірте права запису, диск/іноди, а потім логи.
  2. Перейменуйте перед видаленням. Зберігайте докази, менше жалю.
  3. Виправляйте причину (власність, каталог підготовки, SELinux, тимчасові директорії), а не симптом.
  4. Виберіть операційну модель: або WordPress керує плагінами/темами (і ви підтримуєте це), або ваш пайплайн керує ними (і WordPress перестає пробувати).
  5. Зробіть це повторюваним: оформіть наведені кроки у рукописний runbook і перестаньте щоразу знову вчитися на 9-й годині ранку.

Якщо не зробите нічого іншого: припиніть «виправляти» WordPress випадковим видаленням вмісту wp-content. Це не операції. Це гра на удачу з кращим брендом.

← Попередня
Debian 13: Виправлення «Забагато перенаправлень» в Nginx — корекція канонічних і HTTPS циклів (випадок №71)
Наступна →
ZFS: використання SATA SSD як SLOG — дешева модернізація, яка часто підводить

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