Деякі відмови WordPress не виглядають як відмови. Головна сторінка завантажується, маркетинг задоволений, а потім редактор намагається завантажити зображення і бачить дружнє «Unable to create directory.» Або автопоновлення зависають назавжди. Або сайт після «швидкого виправлення прав», зробленого о 2:00 ночі, повертає 403.
Права файлів — одна з тих проблем, що починаються як паперова рана і закінчуються розбором інциденту. Це ще й одна з небагатьох тем WordPress, де ви можете бути технічно праві і все одно зламати продакшн. Зробімо нудні речі правильно: що повинні означати 755/644, чому ці числа існують і як швидко діагностувати помилки дозволів, не перетворюючи корінь веб-сайту на публічну папку для запису.
Що насправді означають 755/644 (і чому їх постійно повторюють у спільноті WordPress)
Коли кажуть «встановіть для директорій 755, а для файлів 644», це не магічна мантра. Це опис безпечної базової політики для типовго Linux-вебсерверу, де:
- Директоріям потрібен біт execute, щоб їх можна було переходити (так, execute для директорій означає «можна увійти»).
- Файли, як правило, повинні бути читабельні вебсервером, але не записувані всіма.
- Власник може редагувати вміст; вебсервер може читати; випадкові користувачі на тій самій машині не можуть змінювати файли.
Пояснення дозволів в одному абзаці, яке реально можна використати
UNIX-права поділені на три набори бітів: власник, група, інші. Кожен набір може мати r (читання), w (запис), x (виконання). Числова форма — вісімкова: r=4, w=2, x=1. Додаєте — отримуєте цифру. Отже 7 означає rwx, 6 означає rw-, 5 означає r-x, 4 означає r–.
Чому 755 для директорій?
755 означає:
- Власник: 7 (rwx) — може перераховувати, створювати, видаляти, переходити.
- Група: 5 (r-x) — може перераховувати і переходити, але не змінювати.
- Інші: 5 (r-x) — те саме.
Для директорій «read» дозволяє бачити список імен, «execute» дозволяє переходити в неї (і отримувати доступ до файлів, якщо відомі імена), а «write» дозволяє створювати/видаляти/перейменовувати записи. Директорія з 644 зазвичай несправна, бо там бракує execute — ви не зможете увійти в неї, навіть якщо можете «прочитати» список.
А чому 644 для файлів?
644 означає:
- Власник: 6 (rw-) — може редагувати.
- Група: 4 (r–) — може читати.
- Інші: 4 (r–) — може читати.
Це розумно для PHP-файлів, зображень, CSS, JS. Вебсервер повинен їх читати. Він не має редагувати їх на місці.
Частина, яку забувають: власник і процес-користувач
Права — лише половина історії. Інша половина — це: який Unix-користувач запускає вебсервер (або PHP-FPM)? Якщо каталог WordPress належить deploy:deploy, але PHP-FPM працює як www-data, то 755/644 не дозволять завантаження файлів, якщо лише записувані шляхи не налаштовані належним чином за власником чи групою.
Порада: не вирішуйте цю проблему, роблячи все 777. Це не вирішення; це заміна замка наклейкою «будь ласка, не кради».
Жарт #1: Встановити web root у 777 — це як покласти ключ під килимок перед дверима, а потім запостити GPS-координати килимка в Twitter.
Куди WordPress справді потрібно писати (і куди ніколи не слід писати)
WordPress — це додаток, який хоче змінювати себе. Це зручно на шаред-хостингу. У продакшні це рішення з ризиком.
Шляхи, куди WordPress зазвичай потрібно писати
- wp-content/uploads/ — завантаження медіа, масштабування зображень, згенеровані файли.
- wp-content/cache/ (або директорії кеш-плагінів) — залежно від плагінів кешування.
- wp-content/upgrade/ — тимчасово під час оновлень.
- wp-content/languages/ — мовні пакети (іноді).
- wp-content/plugins/ і wp-content/themes/ — лише якщо ви дозволяєте встановлення/оновлення з адмінки.
Шляхи, куди WordPress не повинен писати в добре керованому середовищі
- Файли ядра, як-от
wp-admin/,wp-includes/. - Налаштування сервера, як конфіги Nginx/Apache (очевидно, але я бачив і таке).
- Все поза коренем сайту (якщо це не спеціально спроектовано).
Реальність «FS_METHOD»
Якщо WordPress не може писати туди, куди хоче, він може запитати FTP-дані або збанкрутити оновлення. Це WordPress, що намагається пристосуватися до хостингового середовища. На належно керованому сервері зазвичай обирають один з таких режимів роботи:
- Незмінний код + pipeline для деплою: WordPress не оновлюється на місці; оновлення відбуваються через CI/CD (найбезпечніше для надійності).
- Записуваний тільки wp-content: дозволяє адміністраторам керувати плагінами/темами; ядро залишається лише для читання для веб-користувача (розумний компроміс).
- Все записуване веб-користувачем: найшвидший шлях до жалю (уникати).
Цитата, якою живуть операційні люди
«Надія — це не стратегія.» — перефразована ідея, часто приписувана колам надійності та операцій
Права — це місце, куди йде надія, щоб там померти. Спроектуйте користувачів, спроектуйте записувані шляхи, а потім застосуйте мінімальні привілеї, які дозволяють бізнесу працювати.
Швидкий план діагностики (перевірити 1/2/3)
Якщо ви посеред інциденту, не починайте з рекурсивних chmod. Почніть з найменшого набору перевірок, який ідентифікує тих, хто не може записати (користувач/процес) і шлях, де відбувається збій.
1) Визначте точний шлях, що викликає помилку, і симптом на рівні системного виклику
- Шукайте «Permission denied», «Operation not permitted», «Read-only file system», «No such file or directory», «SELinux is preventing» або «open_basedir restriction.» Це різні проблеми в однаковому костюмі.
- Перевірте логи вебсерверу та PHP на наявність шляху.
2) Визначте, який Unix-користувач робить запис
- Користувач пулу PHP-FPM? Модуль Apache? Робітник Nginx? CLI-користувач, що запускає WP-CLI?
- Підтвердіть через налаштування сервісу та список живих процесів.
3) Перевірте власність + режим + параметри монтування + MAC-контролі
- Власність і режим:
namei -lтаstatна повному шляху. - Параметри монтування:
ro,noexec,nosuidта особливості оверлеїв у контейнерах. - SELinux/AppArmor: якщо ввімкнено, DAC-права можуть виглядати коректними, але все одно блокувати доступ.
Якщо ви пройдете ці три перевірки, зазвичай отримаєте реальне виправлення замість байдарки по боротьбі з правами.
Практичні завдання: команди, виводи, рішення (12+)
Кожне завдання нижче включає: команду, що типовий вивід означає, і рішення, яке ви приймаєте. Вони написані для систем Debian/Ubuntu з www-data; підлаштуйте під ваш дистрибутив (apache, nginx тощо).
Завдання 1: Знайдіть користувача веб/PHP, який реально обслуговує WordPress
cr0x@server:~$ ps -eo user,comm | egrep 'php-fpm|apache2|httpd|nginx' | head
root nginx
www-data nginx
root php-fpm8.2
www-data php-fpm8.2
Що це означає: Робітники Nginx і PHP-FPM працюють як www-data (добре; узгоджено).
Рішення: Директорії/файли, які повинні бути записувані додатком, мають бути доступні для запису www-data (через власника або групову стратегію). Якщо бачите змішаних користувачів (наприклад, Nginx як nginx, PHP-FPM як www-data), оберіть одну модель і узгодьте її.
Завдання 2: Підтвердіть користувача та групу пулу PHP-FPM (джерело правди)
cr0x@server:~$ sudo grep -R "^\s*user\s*=" /etc/php/*/fpm/pool.d/*.conf
/etc/php/8.2/fpm/pool.d/www.conf:user = www-data
cr0x@server:~$ sudo grep -R "^\s*group\s*=" /etc/php/*/fpm/pool.d/*.conf
/etc/php/8.2/fpm/pool.d/www.conf:group = www-data
Що це означає: PHP-скрипти виконуються як www-data:www-data.
Рішення: Будь-яка директорія, в яку WordPress має записувати, повинна бути доступна для запису www-data, напряму або через групові права/ACL.
Завдання 3: Перевірте, який шлях файлової системи використовує WordPress (docroot)
cr0x@server:~$ sudo nginx -T 2>/dev/null | grep -R "root " -n | head
cr0x@server:~$ sudo grep -R "root " -n /etc/nginx/sites-enabled | head
/etc/nginx/sites-enabled/example.conf:12: root /var/www/example/current/public;
Що це означає: Ваш реальний docroot — /var/www/example/current/public, а не те, що ви приблизно пригадуєте з шести місяців тому.
Рішення: Проводьте всі перевірки прав відносно реального шляху, а не старої цілі симовничок чи папки для стейджингу.
Завдання 4: Інспектуйте права в директорії, що падає (швидкий огляд)
cr0x@server:~$ ls -ld /var/www/example/current/public/wp-content/uploads
drwxr-xr-x 2 deploy deploy 4096 Dec 27 09:14 /var/www/example/current/public/wp-content/uploads
Що це означає: Директорія 755, але належить deploy:deploy. Веб-користувач www-data тут — «інші», тому він не може писати.
Рішення: Змініть власника або використайте стратегію груп/ACL, щоб www-data міг писати лише там, де потрібно.
Завдання 5: Перевірте права на проходження по всьому шляху за допомогою namei (ловить помилку з батьківською директорією)
cr0x@server:~$ namei -l /var/www/example/current/public/wp-content/uploads
f: /var/www/example/current/public/wp-content/uploads
drwxr-xr-x root root /
drwxr-xr-x root root var
drwxr-xr-x root root www
drwxr-x--- deploy deploy example
lrwxrwxrwx deploy deploy current -> releases/2025-12-27_0910
drwxr-xr-x deploy deploy releases
drwxr-xr-x deploy deploy 2025-12-27_0910
drwxr-xr-x deploy deploy public
drwxr-xr-x deploy deploy wp-content
drwxr-xr-x deploy deploy uploads
Що це означає: Директорія /var/www/example має 750 (drwxr-x---) і належить deploy:deploy. Навіть якщо ви виправили uploads, www-data може не проходити по дереву.
Рішення: Забезпечте, щоб кожна батьківська директорія була проходжувана користувачем, якому потрібен доступ (часто через груповий execute). Це прихована причина «але uploads — 777 і все одно не працює».
Завдання 6: Перевірте режим файлу та власника точно за допомогою stat
cr0x@server:~$ stat -c '%A %a %U:%G %n' /var/www/example/current/public/wp-config.php
-rw-r----- 640 deploy deploy /var/www/example/current/public/wp-config.php
Що це означає: wp-config.php має 640 і не читається «іншими». Якщо PHP працює як www-data і не в групі deploy, WordPress може впасти з 500 помилкою.
Рішення: Або зробіть його доступним для групи і додайте PHP-користувача в цю групу, або змініть власника належним чином. Не чинимо 644 за замовчуванням, якщо можна зберегти жорсткіші права через правильну групу.
Завдання 7: Підтвердіть членство в групах (чи належить www-data до групи deploy?)
cr0x@server:~$ id www-data
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Що це означає: www-data не входить до групи deploy. Групові читання/записи не працюватимуть, поки ви не відрегулюєте групи або не використаєте ACL.
Рішення: Оберіть: додати www-data до спільної групи (поширено на однотенантних серверах), або використовувати ACL для конкретних шляхів (чистіше для мульти-орендерних середовищ).
Завдання 8: Безпечне виправлення власності для записуваного шляху (тільки uploads)
cr0x@server:~$ sudo chown -R www-data:www-data /var/www/example/current/public/wp-content/uploads
cr0x@server:~$ ls -ld /var/www/example/current/public/wp-content/uploads
drwxr-xr-x 2 www-data www-data 4096 Dec 27 09:14 /var/www/example/current/public/wp-content/uploads
Що це означає: Тепер PHP-користувач може писати в uploads без того, щоб робити весь код бази змінним.
Рішення: Якщо ви використовуєте інструменти деплою, які переключають релізи, розгляньте спільну постійну директорію для uploads і симвлінк на неї; інакше наступний деплой може скинути власність.
Завдання 9: Встановіть базові права (директорії 755, файли 644) без затікання по особливих випадках
cr0x@server:~$ sudo find /var/www/example/current/public -type d -print0 | sudo xargs -0 chmod 755
cr0x@server:~$ sudo find /var/www/example/current/public -type f -print0 | sudo xargs -0 chmod 644
Що це означає: Стандартизує режими. Якщо це «полагодило» сайт, ймовірно хтось раніше зробив рекурсивний chmod у неправильний стан на кшталт 600 або 700.
Рішення: Негайно посиліть захист чутливих файлів (наприклад, wp-config.php) після відновлення бази та забезпечте, щоб записувані директорії залишалися записуваними для потрібного користувача. Базові налаштування — це каркас, а не архітектура.
Завдання 10: Знайдіть файли та директорії, записувані для всіх (сигнал тривоги з безпеки)
cr0x@server:~$ sudo find /var/www/example/current/public -perm -0002 -ls | head
412310 4 drwxrwxrwx 2 www-data www-data 4096 Dec 27 09:15 /var/www/example/current/public/wp-content/cache
Що це означає: Деякі шляхи є world-writable (...rwx для інших). Іноді плагіни так роблять. Іноді люди роблять у паніці.
Рішення: Приберіть запис для всіх, якщо немає явно виправданої причини. Краще використовувати 775 з контрольованим членством у групі або ACL.
Завдання 11: Перевірте атрибут immutable (таємниця «chmod не працює»)
cr0x@server:~$ lsattr -d /var/www/example/current/public/wp-content/uploads
-------------e------- /var/www/example/current/public/wp-content/uploads
Що це означає: Немає прапора immutable. Якщо бачите i в атрибутах, ядро блокуватиме зміни навіть для root.
Рішення: Якщо immutable встановлено випадково, зніміть його для конкретного шляху (обережно), а не для всього дерева.
Завдання 12: Підтвердіть, що файлова система не змонтована лише для читання
cr0x@server:~$ findmnt -no TARGET,OPTIONS /var/www
/var/www rw,relatime
Що це означає: Монтування — читабельно-записувальне. Якщо там було б ro, WordPress ніколи б не зміг завантажити файли незалежно від chmod/chown.
Рішення: Якщо бачите ro, припиніть маніпуляції з правами. Виправляйте питання зі сховищем: перемонтуйте, виправте помилки файлової системи, перевірте оверлей контейнера або зʼясуйте, чому хост перемонтував у режим read-only.
Завдання 13: Перевірте статус SELinux (права можуть виглядати «коректними» й все одно падати)
cr0x@server:~$ sestatus
SELinux status: disabled
Що це означає: SELinux вимкнено; контексти можна ігнорувати.
Рішення: Якщо SELinux в режимі enforcing, потрібно перевірити контексти на записуваних директоріях (і виправити їх), а не ширити Unix-режими.
Завдання 14: Статус AppArmor (поширено на Ubuntu)
cr0x@server:~$ sudo aa-status | head
apparmor module is loaded.
10 profiles are loaded.
Що це означає: AppArmor активний. Профіль може забороняти запис PHP, навіть якщо Unix-права дозволяють.
Рішення: Якщо підозрюєте AppArmor, перевірте логи відмов і відкоригуйте профіль для конкретних шляхів запису. Не «вирішуйте» проблему 777.
Завдання 15: Відтворіть запис від імені PHP-користувача (перевіряє реальну межу прав)
cr0x@server:~$ sudo -u www-data bash -lc 'touch /var/www/example/current/public/wp-content/uploads/.permtest && echo ok'
ok
Що це означає: Користувач додатка може писати. Якщо ця команда падає з «Permission denied», проблема в власності/режимі/ACL/SELinux/параметрах монтування.
Рішення: Використайте цей тест, щоб підтвердити виправлення перед тим, як просити редакторів повторити завантаження.
Завдання 16: Перевірте ACL (коли права виглядають правильно, але працюють не так)
cr0x@server:~$ getfacl -p /var/www/example/current/public/wp-content/uploads | sed -n '1,12p'
# file: /var/www/example/current/public/wp-content/uploads
# owner: www-data
# group: www-data
user::rwx
group::r-x
other::r-x
Що це означає: Додаткових ACL-прав немає. Якщо бачите ACL-записи, вони можуть надавати або обмежувати доступ понад базові біти режиму.
Рішення: В мультикористувацьких середовищах ACL часто є чистішим способом надати www-data право запису без передачі власності.
Завдання 17: Знайдіть нещодавно змінені права (питання «хто це торкав?»)
cr0x@server:~$ sudo find /var/www/example/current/public -printf '%TY-%Tm-%Td %TH:%TM %m %u:%g %p\n' | sort -r | head
2025-12-27 09:16 755 www-data:www-data /var/www/example/current/public/wp-content/uploads
2025-12-27 09:12 640 deploy:deploy /var/www/example/current/public/wp-config.php
Що це означає: Ви бачите, що змінилися останні, а це допомагає корелювати з деплоєм або «швидкими виправленнями».
Рішення: Якщо деплой змінює власність спільних директорій, оновіть скрипти деплою. Дрейф прав — це помилка автоматизації, а не проблема людського навчання.
Поширені помилки: симптом → корінь проблеми → виправлення
1) «Unable to create directory wp-content/uploads/…»
Симптом: Завантаження медіа не вдаються; адмінка WordPress скаржиться на створення директорії.
Корінь проблеми: uploads або батьківська директорія не записувані/непрохідні для PHP-користувача; іноді батьківська — 750 і блокує проходження.
Виправлення: Перевірте за допомогою namei -l. Забезпечте, щоб повний шлях був проходжуваний (x-біт) і що uploads записувальний для PHP-користувача через власника, групу або ACL. Уникайте world-writable.
2) Встановлення плагіну/теми просить FTP-дані
Симптом: WordPress просить FTP, хоча ви на власному сервері.
Корінь проблеми: WordPress не може писати в wp-content/plugins або wp-content/themes, тож він переходить на методи типу FTP.
Виправлення: Вирішіть модель роботи. Якщо дозволяєте оновлення з адмінки, зробіть ці директорії записуваними для PHP-користувача (бажано через групові права/ACL, а не 777). Якщо ні — відключіть файлові записи і виконуйте оновлення через pipeline деплою.
3) 403 Forbidden після «затискання прав»
Симптом: Весь сайт повертає 403; статичні ресурси також можуть не відвантажуватися.
Корінь проблеми: Директорії встановлено на 644 або 600, втрачаючи execute-біт, тож вебсервер не може проходити.
Виправлення: Директорії загалом мають бути 755 (або 750, якщо ви спеціально контролюєте доступ через групу). Використайте find -type d -exec chmod 755 та перевірте з namei -l.
4) 500 Internal Server Error, в логах “failed to open stream: Permission denied” для wp-config.php
Симптом: PHP не може прочитати конфіг; сайт білить екраном.
Корінь проблеми: wp-config.php — 600 або 640 з неправильною групою; PHP-користувач не має прав читання.
Виправлення: Зробіть його читабельним для PHP-користувача через групову стратегію або зміну власника. Уникайте робити його читабельним для всіх, якщо можна; 640 з правильною групою — ок.
5) “Operation not permitted” при запуску chown/chmod
Симптом: Навіть root не може змінити власність, або зміни миттєво скасовуються.
Корінь проблеми: Встановлено immutable, або шлях на файловій системі забороняє chown (деякі мережні монтування), або ви в контейнері з мапінгом ID.
Виправлення: Перевірте lsattr, тип монтування та мапінг UID контейнера. Виправляйте базове обмеження; не лупцюйте інструментами.
6) Все виглядає правильно, але запис все одно падає
Симптом: Режими/власність виглядають правильними; touch від root працює; WordPress все одно не може писати.
Корінь проблеми: Відмови SELinux/AppArmor, монтування лише для читання, або пул PHP-FPM працює під іншим користувачем, ніж очікувалося.
Виправлення: Підтвердіть користувача пулу, перевірте статус MAC-системи/логи, перевірте опції монтування. Тестуйте з sudo -u www-data touch ..., щоб підтвердити реального актора.
Три міні-історії з корпоративного життя з польових боїв за права
Міні-історія 1: Інцидент через хибне припущення
Вони успадкували інсталяцію WordPress, яка «завжди працювала». Новий інженер мав перенести її на свіжевстановлену VM. Nginx попереду, PHP-FPM позаду, сучасний TLS. Чекліст міграції був акуратний: rsync файлів, імпорт БД, оновлення DNS. Запустили вчасно.
Через дві години команда контенту почала завантажувати зображення для кампанії. Кожне завантаження падало. Інженер на чергуванні зробив звичні кроки: chmod -R 755 для директорій, chmod -R 644 для файлів, потім повторив. Все ще не працювало. Хтось запропонував ядерний варіант: chmod -R 777 wp-content. Завантаження відразу запрацювали. Усі зітхнули.
Через два дні скан безпеки виявив компрометацію хоста. Розбір був неприємний, але повчальний. Хибним припущенням було не «755/644 замало». Хибним було «додаток-користувач може пройти по шляху». Докрут root був під /srv/sites/clientA з правами 750 і власником deploy:deploy. Тож PHP-користувач навіть не міг дістатися до wp-content надійно. Зміна на 777 приховала проблему проходження і перетворила решту дерева на майданчик для запису.
Вони виправили це правильно: встановили спільну групу для сайту, зробили лише потрібні піддиректорії групозаписуваними і перевірили проходження за допомогою namei -l. Компроміс був простим: дозволити WordPress писати до uploads і кешу, але тримати код незмінним. Команда безпеки перестала дзвонити. Маркетинг ніколи не дізнався, що майже трапилося — так і має бути.
Міні-історія 2: Оптимізація, що дала назад
Інша компанія хотіла «швидші деплоя». Їхня відповідь — змонтувати wp-content/uploads на мережеву файлову систему, щоб кілька серверів додатка могли ділитися медіа. Мета розумна, але реалізація ризикована. Використали монтування, яке не поводилося як локальний ext4: мапінг власності непослідовний, і семантика chmod/chown була не такою, як команда очікувала.
Перший симптом проблеми: перетворення зображень іноді падало. Мініатюри з’являлися для одних завантажень і не зʼявлялися для інших. Редактори звинувачували WordPress. Інженери — кеш-плагін. Логи показували Permission denied для файлів, які виглядали записуваними на одній хості, але не на іншій.
«Оптимізація» стала також регресією надійності. Налаштування мережевого сховища мало тонкі відмінності трансляції прав між вузлами, і деплой іноді створював директорії з umask за замовчуванням, які робили їх незаписуваними для PHP-користувача на половині флоту. Це не була постійна помилка. Це було гірше: вона була переривчастою.
Кінцеве виправлення — не «chmod сильніше». Вони стандартизували опції монтування, примусили створювати директорії з setgid на спільній директорії (щоб нові файли успадковували правильну групу), і додали перевірку здоровʼя, що виконувала тест запису як PHP-користувач на кожному вузлі. Також задокументували модель експлуатації: якщо ви ділите uploads, треба поводитися з цим як із системою зберігання, а не як із звичайною папкою.
Міні-історія 3: Нудна, але правильна практика, що врятувала ситуацію
Ритейл-організація запускала WordPress як частину більшого стеку. Нічого особливого. Команда робила одну дуже неефектну, але правильну річ: робили код WordPress у деплої лише для читання і зберігали uploads поза директорією релізу на постійному шляху. Деплоям створювали симвлінк з public/wp-content/uploads на /srv/wordpress-uploads/site1.
Потім у новинах з’явилася вразливість плагіну. Не теоретична: ті боти, що штампують інтернет у пошуках записуваних PHP-файлів. Їх вже патчували в стейджингу, але продакшн-патч чекали вікна технічного обслуговування через те, що маркетинг в середині кампанії (маркетинг завжди в середині кампанії).
В ті дні боти зробили своє. Вони знайшли сторінку логіна і спробували. Знайшли кінцеві точки плагіну і спробували. Вони потрапили у вразливий шлях, але не змогли зберегти веб-шелл там, де хотіли, бо директорії з кодом не були записувані PHP-користувачем. Uploads були записувані, але виконання PHP з uploads було заблоковано на рівні вебсерверу.
Реакція на інцидент була спокійною. Ніякої суєти з diff тисяч файлів, ніякої археології «цей файл має існувати чи ні». Вони запатчили, поміняли секрети і продовжили роботу. Нудна практика — відокремлення mutable і immutable даних — перетворила потенційне скомпрометування на фоновий шум.
Жарт #2: Права — як дрес-код: занадто суворо — ніхто не працює, занадто вільно — раптом «casual spear-phishing Friday».
Цікаві факти та історичний контекст (бо Unix старий і примхливий)
- Біт виконання для директорій з’явився ще до більшості веб‑програм. Він означає «пошук» (traverse), а не «запуск». Намір заплутувати, очевидно.
- Вісімкова нотація стала людським скороченням, бо біт прав природно групуються по три. Це не довільно; це система числення, що пасує під дані.
- «Sticky bit» на директоріях (як у
/tmp) існує тому, що багатокористувацьким системам потрібні спільні записувані простори без можливості видаляти файли один одного. - Setgid на директоріях — класичний трюк для спільної роботи: нові файли успадковують групу директорії. Це старше, ніж більшість CI/CD pipeline і часто надійніше.
- Umask — причина, чому два сервери з однаковими chmod командами все одно можуть створювати файли з різними правами. Це маска за замовчуванням на рівні процесу, а не властивість файлової системи.
- Історична модель Apache з багатьма процесами вплинула на багато порад «просто chown на www-data». PHP-FPM і рантайми контейнерів змінили характер проблеми.
- NFS і семантика прав спалили безліч команд. Деякі монтування маплять root у nobody (root-squash), а деякі не підтримують Unix-власність так, як ви припускаєте.
- SELinux був створений тому, що дискреційний контроль доступу (Unix-права) не може виразити «цей процес може писати тільки в ці промарковані директорії». WordPress часто спрацьовує на цю вищу систему.
- Механізм самоновлення WordPress походить із його коренів у шаред-хостингу. Сучасна продакшн-практика часто вимикає мутабельність на місці і ставиться до коду як до артефакту.
Контрольні списки / покроковий план
Покроково: виправити помилки дозволів, не роблячи все записуваним
- Підтвердіть актора: визначте runtime-користувача (
www-data,nginxтощо) черезpsі конфіг пулу PHP-FPM. - Знайдіть шлях, що падає: з помилки WordPress, логів PHP або вебсерверу. Не вгадуйте.
- Перевірте проходження: запустіть
namei -lна шляху, що падає, і шукайте батьківську директорію, яка блокує execute. - Визначте модель:
- Незмінний код + CI/CD оновлення, або
- Записуваний
wp-content(або його частини), або - Повністю мутабельний (не робіть цього).
- Виправте власність лише для записуваних шляхів: зазвичай
wp-content/uploadsі можливо директорії кешу. Використовуйтеchown -R www-data:www-dataдля цих шляхів. - Застосуйте базові режими: директорії 755, файли 644 для дерева коду. Потім посиліть захист для особливих файлів (
wp-config.php). - Зробіть тест запису від імені PHP-користувача:
sudo -u www-data touch ...у тій директорії, яка падала. - Перевірте монтування/MAC: переконайтеся, що файлова система —
rw; перевірте SELinux/AppArmor за потреби. - Запобігайте дрейфу: закладьте бажану власність/режим у автоматизацію деплою. Якщо деплой скидає права, ви знову тут за тиждень.
Чекліст: загартовані, але функціональні права WordPress (типовий односерверний VM)
- Директорії коду: належать
deploy(або root), читаються веб-користувачем, не записуються веб-користувачем. wp-content/uploads: записувана веб-користувачем; бажано окремий постійний шлях з контрольованими правами.- Немає директорій, записуваних для всіх у документ-руті.
- Опційно заблокуйте виконання PHP в uploads на рівні вебсерверу (це не біт прав, але важливо).
- Використовуйте групову стратегію або ACL замість «все належить www-data», якщо люди мають редагувати файли.
Чекліст: мультисерверна або спільна памʼять (тут все ускладнюється)
- Уніфікуйте runtime-користувача по всіх вузлах.
- Використайте setgid на спільних директоріях, щоб зберегти групу.
- Перевірте опції монтування та мапінг власності.
- Додайте на рівні вузла тест запису для uploads у health checks.
- Якщо архітектура дозволяє — віддавайте перевагу об’єктному сховищу для uploads; спільні POSIX монтування дорожчі в експлуатації.
FAQ
1) Чи 755 і 644 завжди правильні для WordPress?
Ні. Це безпечна базова політика для читабельності і проходження. Правильність залежить від власності, PHP-користувача та того, які директорії мають бути записуваними. У деяких конфігураціях 750/640 з належними групами буде краще.
2) Чому директоріям потрібен біт виконання?
Тому що execute на директорії означає «можна переходити/шукати в ній». Без нього процес не зможе отримати доступ до файлів всередині, навіть якщо може прочитати список.
3) Чи має wp-config.php бути 644?
Часто це працює, але це не найкращий дефолт. Віддавайте перевагу 640 з правильною групою, щоб тільки власник і група веб/PHP могли його читати. Якщо сервер однотенантний і ви більше довіряєте веб-процесу, тримайте його більш закритим.
4) Чи безпечно робити chown всього на www-data?
Це функціонально і поширено, але також простий спосіб дозволити скомпрометованому веб-процесу змінювати код додатка. Якщо вам важлива інкапсуляція, тримайте код під власністю deploy-користувача і давайте запис тільки uploads/cache.
5) Оновлення WordPress падають, якщо я не зроблю плагіни/теми записуваними. Що робити?
Виберіть політику. Якщо хочете «клікаю‑та‑оновлюю», потрібно дозволити запис у директорії плагінів/тем (ідеально через групові права/ACL, а не 777). Якщо хочете безпечнішу систему, відключіть оновлення на місці і деплойте зміни плагінів/тем через CI/CD.
6) У чому різниця між «Permission denied» і «Operation not permitted»?
«Permission denied» зазвичай означає, що поточний користувач не має прав (режими/ACL/MAC). «Operation not permitted» часто вказує на сильніше обмеження: immutable-атрибут, правила файлової системи або обмеження контейнера/неймспейсу користувачів.
7) Мої права коректні, але завантаження все одно падають. Що ще може бути?
Поширені причини: файлова система змонтована як read-only, SELinux/AppArmor відкидає доступ, неправильний пул PHP-FPM, диск заповнений (записи не проходять), або батьківська директорія блокує проходження. Перевірте це перед тим, як знову змінювати режими.
8) Чи потрібен 775 замість 755?
Лише якщо ви свідомо використовуєте групову співпрацю (наприклад, deploy-користувач і PHP-користувач поділяють групу). 775 надає групі право запису. Це нормально, коли членство в групі контрольоване і навмисне.
9) А як щодо 700 для директорій?
700 — відмінно для приватних директорій, доступних тільки власнику. Це погано для веб-руту, якщо веб-процес не є власником. Використовуйте 750, якщо хочете «лише власник + група».
10) Як зупинити дрейф прав після деплоїв?
Зробіть це відповідальністю автоматизації. Переконайтеся, що скрипти деплою встановлюють власність/режими при створенні релізу, зберігають постійні директорії (uploads) і перевіряють через тест запису як runtime-користувач.
Висновок: наступні кроки, які не розбудять вас о 3:00
Більшість інцидентів з правами WordPress — не про магічні числа. Вони про невідповідність між тим, хто пише, і куди ви випадково заблокували проходження або власність. 755/644 — добрі значення за замовчуванням, але це не стратегія.
Зробіть наступне
- Запишіть вашу модель роботи: чи може WordPress оновлювати себе, чи ні? Вирішіть і забезпечте це правами.
- Відокремте mutable від immutable: тримайте uploads (і, можливо, кеш) записуваними; тримайте ядро та більшість коду для читання для веб-користувача.
- Автоматизуйте бажаний стан: скрипти деплою повинні встановлювати власність/режими і перевіряти їх. Ручний chmod породжує фольклор про права.
- Додайте перевірку здоровʼя: простий тест запису від імені PHP-користувача в uploads виявить дрейф до того, як редактори його помітять.
Якщо ви це зробите, 755/644 стануть тим, чим вони й мали бути завжди: базовою політикою, яку ви розумієте, а не ритуалом, який повторюють.