403 Forbidden — це найменш інформативний різновид «ні». Це стек відповідає вам, прямим текстом, що він зрозумів запит і все одно відхилив його — часто не пояснюючи, який саме компонент це зробив.
У світі WordPress це відхилення може походити звідусіль: права на файлову систему, правило веб‑сервера, яке ви забули, WAF, що вирішив, що ваше тіло POST схоже на SQLi, CDN, який «захищає» origin, або плагін безпеки, що прокинувся з бойовим настроєм. Цей посібник покаже, як вказати на правильний шар і виправити проблему, не відкривши сайт усім бажаючим.
Що насправді означає 403 (і чому це не одне й те саме)
HTTP 403 означає, що сервер (або щось, що поводиться як сервер) відмовляється авторизувати запит. Важлива частина: «сервер» може бути CDN‑edge, зворотний проксі, ваш origin веб‑сервер, модуль WAF або навіть сам WordPress через плагін, що повертає 403.
Типові сценарії:
- 403 на edge: CDN/WAF блокує до того, як трафік доходить до origin. В журналах origin може бути чисто, бо запит просто не дійшов.
- 403 на проксі/веб‑сервері: правила Nginx/Apache, контролі доступу, відмови за шляхом, управління відображенням каталогів, неправильна автентифікація або правила клієнтських TLS‑сертифікатів.
- 403 від файлової системи: користувач веб‑сервера не може прочитати файл або пройти по каталогах. Nginx часто логуватиме «permission denied» і повертатиме 403/404 залежно від конфігурації.
- 403 від логіки додатка: ядро WordPress рідко так робить для публічних сторінок, але плагіни безпеки та кастомні mu‑plugin можуть це здійснювати.
Операційно, 403 — це скоріше злам переговорів між «хто ви» і «до чого вам дозволено доступ». Виправлення полягає в тому, щоб з’ясувати, який актор наклав вето, і скорегувати найменший потрібний дозвіл або правило.
Цитата, яку варто тримати при собі під час триажу доступів: paraphrased idea
від Werner Vogels: «Все ламається постійно; проектуйте й експлуатуйте так, ніби відмови — нормальний стан». Цей підхід працює і для прав доступу, і для правил WAF — очікуйте випадкових відмов і майте швидкі шляхи для їх локалізації.
Жарт #1: 403 — це як охоронець у нічному клубі, який не скаже вам дрес‑код — просто заявить, що ваш «запит не бажаний».
Швидкий план діагностики (перший/другий/третій)
Це найкоротша дорога до «хто заблокував» без блукань по всіх конфігураційних файлах планети.
Перший: визначте, де згенеровано 403
- Перевірте заголовки відповіді з клієнта: шукайте маркери CDN/WAF і сигнатури серверів. Якщо бачите CDN‑заголовок або «Server: cloudflare» — вважайте, що блок на edge, доки не доведено протилежне.
- Перевірте, чи доходить запит до origin: підглядайте access‑логи origin під час відтворення. Відсутність запису часто означає блокування на edge або неправильний DNS/шлях.
- Порівняйте з двох мереж: ваш офісний IP може бути заблокований, тоді як інші працюють нормально (або навпаки).
Другий: класифікуйте поверхню блокування
- Чи лише wp-admin/wp-login.php? Думайте про правила WAF, захист від брутфорсу, гео/IP allowlist або обмеження доступу через автентифікацію.
- Чи лише uploads/статичні файли? Думайте про права файлової системи, блоки location у Nginx, захист від хотлінкінгу або відмову за розширенням/MIME.
- Чи лише POST‑запити? Думайте про ModSecurity/OWASP CRS, обмеження розміру тіла запиту або плагіни, що маркують payload/nonce.
- Чи переривчасто? Думайте про rate limiting, fail2ban, бот‑менеджмент або проксі‑шар з непослідовною конфігурацією.
Третій: виправте найменш тривкій шар
- Якщо це edge/WAF: почніть з логів правил і тестування «обхід для цього URI» перед тим, як змінювати права origin.
- Якщо це веб‑сервер: виправте конкретні
location/<Directory>правила або області автентифікації — не використовуйте бездумне «дозволити всім». - Якщо це файлова система: виправте власність, біт доступу і ACL для мінімального піддерева.
- Якщо це WordPress/плагін: безпечно відключіть проблемний плагін або додайте правило дозволу для конкретного маршруту.
Факти та історія: чому 403 повторюється
Трохи контексту допомагає, бо 403 — це не лише WordPress‑феномен, а побічний продукт еволюції вебу.
- Факт 1: Статус‑код «403 Forbidden» визначений стандартами HTTP ще в ранніх RFC; він призначений для зрозумілих запитів, яким відмовлено в авторизації, а не для некоректних запитів (для них існує 400).
- Факт 2: Модель Apache з
.htaccessпопуляризувала перевагу директив на рівні директорій; вона також породила самостійні 403, коли rewrite або deny опинилися в невідповідній папці. - Факт 3: Стиль конфігурації Nginx (
locationпріоритети, regex‑локації, internal redirects) робить його швидким і передбачуваним — до тих пір, поки одинdeny all;у невірному блоці не стане невидимим «сторожем». - Факт 4: ModSecurity з’явився, щоб захищати веб‑додатки від типових атак без переписування коду; OWASP CRS чудовий, але false positive — постійний податок, на який треба закладатися.
- Факт 5: XML‑RPC WordPress існував для віддалених клієнтів публікації; він став улюбленцем для брутфорсу й ампліфікації, тож багато WAF‑шаблонів блокують або сильно лімітують його.
- Факт 6: Ендпоїнти REST API (
/wp-json/) тепер активно використовуються сучасними WP‑фічами і headless‑сценаріями; їх блокування ламає речі, що виглядає як «випадковий 403 адміністрування». - Факт 7: «Хотлінк‑захист» (deny‑за referer) був поширеним захистом трафіку на початку 2000‑х; він досі викликає 403 на зображеннях, коли сайти переходять за CDN або змінюють домени.
- Факт 8: Багато хостинг‑панелей колись радили
777як «фікс». Це спадок із нестабільних епох shared‑hosting і його час залишити на горищі. - Факт 9: Fail2ban‑подібні IP‑бані починалися як практичний захист SSH і згодом поширилися на HTTP; вони ефективні, але можуть також заблокувати моніторинг або NAT офісу.
Пошарове налагодження: визначення, хто сказав “forbidden”
Якщо ви ставите 403 як «WordPress зламався», ви зміните не те і додасте ризиків. Трюк — пройти шлях запиту:
- Клієнт (браузер, бот, API‑клієнт)
- DNS (чи потрапляєте ви на правильний edge/origin?)
- CDN/WAF (політики edge, бот‑менеджмент, гео/IP‑allowlist)
- Балансувальник/зворотний проксі (ACL, правила шляху, автентифікація)
- Веб‑сервер (правила Nginx/Apache, rewrite, auth, internal locations)
- ОС + файлова система (власність, біт доступу, ACL, SELinux/AppArmor)
- PHP‑FPM + WordPress (блоки плагінів, авторизація на рівні додатка, nonce/CSRF)
- Залежності вниз по ланцюжку (object storage для uploads, SSO, стороння автентифікація)
Більшість 403 стають очевидними, як тільки ви встановите шар. Тож перше завдання — атрибуція.
Практичні завдання: команди, виводи та рішення (12+)
Це реальні кроки. Кожне завдання включає: команду, що означає вивід, і яке рішення прийняти далі. Використовуйте їх по черзі, поки не визначите винуватця.
Завдання 1: Відтворіть і зафіксуйте заголовки (визначити edge vs origin)
cr0x@server:~$ curl -I https://example.com/wp-login.php
HTTP/2 403
date: Fri, 26 Dec 2025 10:12:33 GMT
content-type: text/html; charset=UTF-8
server: cloudflare
cf-ray: 85a1b2c3d4e5f678-LHR
Що це означає: Відповідь сервісує Cloudflare (або принаймні edge ідентифікує себе). 403, ймовірно, згенерований на edge.
Рішення: Не чіпайте поки права Nginx. Перейдіть до логів і правил WAF/CDN; також підтвердіть, чи запит доходить до origin (Завдання 2).
Завдання 2: Підтвердьте, чи origin бачить запит
cr0x@server:~$ sudo tail -f /var/log/nginx/access.log
192.0.2.10 - - [26/Dec/2025:10:12:33 +0000] "GET / HTTP/1.1" 200 15432 "-" "curl/8.5.0"
Що це означає: Ви бачите запити /, але не /wp-login.php (у той час як клієнт отримує 403). Це узгоджується з блокуванням лише певного шляху на edge.
Рішення: Зосередьтеся на політиці edge/WAF. Якщо ж заблокований запит видно в access‑логах, переходьте до Завдань 4–9.
Завдання 3: Обійдіть CDN (влучте в origin напряму) для ізоляції шарів
cr0x@server:~$ curl -I --resolve example.com:443:203.0.113.20 https://example.com/wp-login.php
HTTP/2 200
date: Fri, 26 Dec 2025 10:13:10 GMT
content-type: text/html; charset=UTF-8
server: nginx
Що це означає: Origin повертає 200, тоді як публічний шлях повертає 403. Це проблема політики edge/WAF, а не прав WordPress.
Рішення: Виправляйте правила WAF або бот‑захист; уникайте панічного «chmod всього».
Завдання 4: Перевірте лог помилок Nginx на підказки про права/доступ
cr0x@server:~$ sudo tail -n 50 /var/log/nginx/error.log
2025/12/26 10:14:22 [error] 1123#1123: *884 open() "/var/www/example.com/wp-admin/admin.php" failed (13: Permission denied), client: 192.0.2.10, server: example.com, request: "GET /wp-admin/ HTTP/1.1", host: "example.com"
Що це означає: Класичне відмовляння файлової системи (errno 13). Nginx не зміг прочитати цільовий файл.
Рішення: Перевірте власність/біти/ACL/SELinux. Починайте з Завдань 6–8 і Завдання 10.
Завдання 5: Переконайтеся, що vhost конфіг не явно не забороняє шляхи
cr0x@server:~$ sudo nginx -T 2>/dev/null | sed -n '/server_name example.com/,$p' | sed -n '1,160p'
server {
server_name example.com;
root /var/www/example.com;
location ~* \.(php|phtml)$ { include snippets/fastcgi-php.conf; }
location ~* /wp-admin/ { deny all; }
}
Що це означає: Хтось додав deny all для /wp-admin/. Nginx робить саме те, що йому сказали.
Рішення: Видаліть або звузьте правило deny (наприклад, дозволити IP адміністраторів), потім перезавантажте Nginx (Завдання 9). Не чіпайте права файлової системи; ймовірно, там усе в порядку.
Завдання 6: Перевірте права файлів та директорій на проблемному шляху
cr0x@server:~$ namei -l /var/www/example.com/wp-admin/admin.php
f: /var/www/example.com/wp-admin/admin.php
drwxr-x--- root root /
drwxr-x--- root root var
drwxr-x--- root root www
drwxr-x--- root root example.com
drwxr-x--- root root wp-admin
-rw-r----- root root admin.php
Що це означає: Весь шлях власний root:root з суворими правами на виконання для директорій (750). Якщо Nginx працює під www-data, він не може пройти по шляхах.
Рішення: Виправте власність на користувача/групу веб‑сервера (Завдання 8) або підкоригуйте членство груп і права безпечно. Уникайте світово‑записувальних рішень.
Завдання 7: Підтвердьте користувача веб‑сервера
cr0x@server:~$ ps -o user,group,cmd -C nginx | head -n 5
USER GROUP CMD
root root nginx: master process /usr/sbin/nginx
www-data www-data nginx: worker process
www-data www-data nginx: worker process
Що це означає: Worker‑процеси працюють як www-data. Це ідентичність, яка має читати й проходити WordPress‑древо.
Рішення: Переконайтеся, що директорії мають принаймні bit execute для www-data (або для його групи) і файли читабельні, потім повторно протестуйте.
Завдання 8: Виправте власність і базові дозволи (безпечні дефолти)
cr0x@server:~$ sudo chown -R www-data:www-data /var/www/example.com
cr0x@server:~$ sudo find /var/www/example.com -type d -exec chmod 755 {} \;
cr0x@server:~$ sudo find /var/www/example.com -type f -exec chmod 644 {} \;
Що це означає: Директорії стають прохідними, файли — читабельними. WordPress все ще зможе писати туди, де потрібно (пізніше можна звузити, дозволивши запис лише в конкретні директорії, як-от wp-content/uploads).
Рішення: Повторно протестуйте. Якщо 403 залишається, а «permission denied» зник, переходьте до правил веб‑сервера або SELinux/AppArmor.
Завдання 9: Валідність і перезавантаження Nginx/Apache після змін конфігів
cr0x@server:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
cr0x@server:~$ sudo systemctl reload nginx
Що це означає: Конфіг валідний і перезавантажено без скидання з’єднань (reload дружніший за restart).
Рішення: Повторіть тест на конкретному URL. Якщо 403 залишається, знову проаналізуйте логи — не припускайте один і той же корінь проблеми.
Завдання 10: Перевірте статус SELinux і аудиту відмов (Linux‑специфічна «невидима стіна»)
cr0x@server:~$ getenforce
Enforcing
cr0x@server:~$ sudo ausearch -m avc -ts recent | tail -n 5
type=AVC msg=audit(1766744162.112:421): avc: denied { read } for pid=2314 comm="nginx" name="wp-config.php" dev="xvda1" ino=393231 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file permissive=0
Що це означає: SELinux відмовляє Nginx у читанні файлів, позначених default_t. Це часто повертає поведінку схожу на 403/500 в залежності від стеку.
Рішення: Виправте контексти (Завдання 11). Не вимикайте SELinux у продакшені просто тому, що він «кричить».
Завдання 11: Відновіть правильні SELinux‑контексти для веб‑кореня
cr0x@server:~$ sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/example.com(/.*)?"
cr0x@server:~$ sudo restorecon -Rv /var/www/example.com
restorecon reset /var/www/example.com context unconfined_u:object_r:default_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0
Що це означає: Файли тепер мають мітки, що дозволяють домену httpd їх читати.
Рішення: Повторно протестуйте. Якщо WordPress має записувати uploads, встановіть запис тільки для тієї директорії з відповідним контекстом (не для всього дерева).
Завдання 12: Виявлення блокувань ModSecurity (403 з відчуттям «Access denied»)
cr0x@server:~$ sudo tail -n 30 /var/log/apache2/modsec_audit.log
--d8a3c0f4-H--
Message: Access denied with code 403 (phase 2). Operator GE matched 5 at TX:anomaly_score.
[id "949110"] [msg "Inbound Anomaly Score Exceeded"] [tag "OWASP_CRS"]
Action: Intercepted (phase 2)
Що це означає: ModSecurity/OWASP CRS заблокував запит. Часто спрацьовує на певні POST‑payloads, REST‑виклики або дивні query‑рядки.
Рішення: Біліть вузько: конкретний rule ID для конкретного шляху, або налаштуйте поріг аномалій для цього vhost. Не відключайте WAF глобально, якщо не прагнете інцидент‑реакції.
Завдання 13: Перевірте, чи ваш IP заблокований fail2ban (сценарій «чому лише я?»)
cr0x@server:~$ sudo fail2ban-client status
Status
|- Number of jail: 3
`- Jail list: sshd, nginx-http-auth, wordpress-login
cr0x@server:~$ sudo fail2ban-client status wordpress-login
Status for the jail: wordpress-login
|- Filter
| |- Currently failed: 1
| `- Total failed: 37
`- Actions
|- Currently banned: 1
`- Banned IP list: 192.0.2.10
Що це означає: Ваш IP забанено на хості. Ви побачите 403/444/обриви з’єднання залежно від конфігурації jail.
Рішення: Розбаньте й відкоригуйте пороги або додайте в allowlist IP офісу/VPN (Завдання 14). Також перевірте, чи ваш моніторинг не генерує помилкові спроби входу.
Завдання 14: Розбанити IP і додати allowlist (обережно)
cr0x@server:~$ sudo fail2ban-client set wordpress-login unbanip 192.0.2.10
1
cr0x@server:~$ sudo grep -R "ignoreip" -n /etc/fail2ban/jail*.conf
/etc/fail2ban/jail.local:3:ignoreip = 127.0.0.1/8 203.0.113.0/24
Що це означає: IP розбанено; allowlist існує в jail.local. «1» означає успіх.
Рішення: Додавайте лише стабільні довірені діапазони. Якщо ваш офіс змінює IP щодня, використовуйте VPN з відомим egress замість розкидання випадкових адрес по всіх конфигах.
Завдання 15: Перевірте, чи WordPress повертає 403 (рівень додатка)
cr0x@server:~$ curl -s -o /dev/null -w "%{http_code}\n" https://example.com/wp-json/wp/v2/users
403
Що це означає: REST‑ендпоїнт повертає 403. Це може бути очікувано (для неавторизованих користувачів заборонено перелік), або плагін блокує всі REST‑виклики.
Рішення: Порівняйте з відомим робочим ендпоїнтом, як-от /wp-json/ індекс, і протестуйте з автентифікацією. Якщо адмінський інтерфейс теж падає — перевірте плагіни безпеки й mu‑plugins.
Завдання 16: Тимчасово відключіть плагіни без wp-admin
cr0x@server:~$ cd /var/www/example.com
cr0x@server:~$ sudo mv wp-content/plugins wp-content/plugins.disabled
cr0x@server:~$ sudo mkdir wp-content/plugins
Що це означає: WordPress бачить «немає плагінів» і повертається до поведінки ядра. Це найчистіший A/B‑тест, коли wp-admin недоступний.
Рішення: Якщо 403 зникає — відновіть плагіни і виконуйте бісекцію: вмикайте пакетами, доки не знайдете кривий. Якщо 403 лишається — плагіни не причина.
Завдання 17: Перевірте .htaccess на deny (Apache або Nginx+htaccess через панелі)
cr0x@server:~$ sudo grep -nE "deny from|require all denied|RewriteRule" /var/www/example.com/.htaccess
12:Require all denied
Що це означає: Існує жорстка відмова. Іноді її лишає «maintenance lockdown» або інструмент міграції.
Рішення: Видаліть або звузьте (наприклад, захистити лише staging‑директорію). Потім перезавантажте Apache і повторно протестуйте.
Завдання 18: Перевірте конфігурацію авторизації Apache і ефективний vhost
cr0x@server:~$ sudo apachectl -S | sed -n '1,80p'
VirtualHost configuration:
*:80 example.com (/etc/apache2/sites-enabled/example.conf:1)
ServerRoot: "/etc/apache2"
Main DocumentRoot: "/var/www/html"
Що це означає: Підтверджує, який vhost обслуговує домен. 403 часто з’являється через редагування не того файлу або не того vhost.
Рішення: Відкрийте вказаний конфіг і шукайте <Directory> блоки з Require all denied або відсутністю Require all granted.
Специфічні для WordPress сценарії помилок 403
WordPress притягує проблеми з правами, бо охоплює публічні сторінки, адмінку, REST API, обробку завантажень і багато стороннього коду. Ось звичні підозрювані — і як вони проявляються.
wp-admin повертає 403, головна сторінка працює
Часто це навмисне блокування від:
- WAF‑шаблону, що блокує
wp-adminдля не allowlist‑них IP - Блоку Nginx/Apache «secure admin», доданого під час інциденту і ніколи не видаленого
- Jail fail2ban, що спрацював після повторних спроб входу
- Плагіну безпеки (Wordfence, iThemes та ін.) у режимі «lockdown»
Що робити: Визначте, чи origin бачить запит. Якщо бачить — шукайте в конфігах блоки по wp-admin і перевіряйте WAF/fail2ban. Не шукайте права файлової системи, якщо логи не показують (13: Permission denied).
wp-login.php повертає 403 або цикл редиректів
Типові причини:
- Бот‑захист вважає ваш вхід ботом; блокує на основі JS‑challenge/куків
- WAF бачить патерни credential stuffing; лімітує або відхиляє
- Інспекція тіла POST маркує паролі як «атака» (false positive трапляються)
- Неправильні заголовки на зворотному проксі викликають проблеми з автентифікацією/куками, що може виглядати як 403 після редиректів
Що робити: Спробуйте з іншого IP і з чистим профілем браузера. Перевірте логи WAF на ідентифікатор запиту і rule ID. Якщо задній плагін, відключіть через файлову систему як у Завданні 16.
Uploads (wp-content/uploads) повертають 403
Це зазвичай нудно. Нудно — добре.
- Права директорії не дозволяють проходження або читання
- Блоки location у Nginx відмовляють «прихованим» файлам, але ваші правила занадто широкі
- Хотлінк‑захист відкидає змінений/відсутній Referer
- Файли переїхали в object storage, а origin відхиляє прямий доступ
Що робити: Перевірте окремий файл за допомогою curl‑заголовків і підтвердьте, що це статичний файл, який сервить веб‑сервер, а не PHP. Потім перевірте права та пріоритет location у Nginx. Якщо використовуєте offload‑плагіни, перевірте логіку підписаних URL і політики бакету.
wp-json або admin-ajax повертають 403
Це ламає редактори, кастомайзери тем, деякі кеш‑плагіни та headless‑фронтенди. Типові винуватці:
- WAF‑правило блокує
/wp-json, бо вважає це «API‑ендпоїнтом» - Плагін безпеки блокує REST API для неаутентифікованих (іноді надто агресивно)
- Змішаний контент або проблеми з proxy‑заголовками викликають збої перевірки nonce, що може проявлятися як 403
Що робити: Протестуйте індекс /wp-json/ спочатку; порівняйте відповіді з автентифікацією та без неї; вирішіть, чи блок — навмисна політика, чи помилка.
WAF/CDN/інструменти безпеки: як виготовляються 403
WAF потрібні. Вони також упевнені у собі. Ваше завдання — зробити їх упевненими й точними одночасно.
Edge 403 vs origin 403: чому це важливо
403, згенерований на edge, буде:
- Відсутній у логах origin access
- Мати часто edge‑специфічні заголовки і ray/request ID
- Іноді показувати брендовану сторінку блокування
403, згенерований на origin, буде:
- З’являтися в Nginx/Apache access‑логах зі статусом 403
- Зазвичай мати корельований контекст в error‑логах («permission denied», «access forbidden by rule», помилки автентифікації)
- Відтворюватися при обході edge (Завдання 3)
ModSecurity/CRS false positive: робіть з цього задачу дебагу, а не війну релігій
False positive зазвичай сплескують, коли ви:
- Розгортаєте новий плагін, який постить JSON або великі форми
- Вмикаєте page builder, що надсилає складні серіалізовані payloads
- Додаєте headless‑ендпоїнти або кастомні REST‑маршрути
Стратегія виправлення:
- Визначте rule ID, що блокує (Завдання 12).
- Підтвердіть, що запит легітимний (не білитьте реальну атаку).
- Вимкніть або підлаштуйте конкретне правило для конкретного URI чи параметра.
- Тримайте аудит. «Ми відключили WAF» — це не політика; це визнання проблеми.
Rate limits і бот‑менеджмент
Rate limiting часто повертає 403 (або 429, залежно від вендора). Складність у тому, що його можуть запускати:
- Реальні атакувальники (добре)
- Ваші монітори аптайму (незручно)
- Навантажувальні тести (передбачувано)
- Мобільні провайдери з NAT (коли один IP — тисячі користувачів)
Більшість провайдерів дозволяють режим «challenge» замість жорсткого блоку. Віддавайте перевагу challenge для анонімного трафіку; жорсткий блок для відомо зловмисних шляхів, як‑от абусивні XML‑RPC патерни.
Плагіни безпеки і «допоміжні» локдауни
Плагіни безпеки WordPress можуть повертати 403 зсередини PHP ще до того, як Nginx/Apache зможуть допомогти. Їхні логи іноді лежать у wp-admin (до якого ви не маєте доступу) — і це … дизайн‑вибір.
Жарт #2: Єдине, що більш наполегливе, ніж брутфорс‑бот — це плагін безпеки, що заблокував вас за п’ять хвилин до демонстрації.
Операційний підхід:
- Тримайте конфіги плагінів у контролі версій де можливо (або хоча б задокументуйте).
- Знайте «kill switch» на файловій системі (Завдання 16).
- Віддавайте перевагу інфраструктурним контролям (WAF, rate limit) для загроз і використовуйте плагіни для WP‑специфічного загартування.
Поширені помилки: симптом → корінь → виправлення
Ці випадки забирають години, бо симптом виглядає як щось інше.
1) Лише один офіс/VPN отримує 403; інші працюють
Симптом: Маркетинг каже «сайт не працює», але ваш телефон у LTE працює.
Корінь: IP‑бан (fail2ban, репутація WAF, бот‑скор) проти NAT/VPN виходу офісу.
Виправлення: Перевірте бан (Завдання 13), розбаньте (Завдання 14), потім створіть стабільну allowlist‑стратегію. Якщо вам треба білити людей, використовуйте VPN з передбачуваним egress.
2) wp-admin 403 після зміни «жорсткого» загартування
Симптом: Головна сторінка 200. Адмін‑сторінки 403.
Корінь: Nginx location або Apache <Directory> deny правило застосоване надто широко (Завдання 5, Завдання 18), або WAF‑path правило.
Виправлення: Видаліть deny або звужуйте його до правильного IP‑діапазону і методу. Перевірте пріоритети в Nginx (regex vs prefix). Перезавантажте й повторно протестуйте.
3) Статичні файли 403 після міграції або відновлення
Симптом: Зображення/CSS повертають 403; PHP‑сторінки можуть і дальше рендеритись.
Корінь: Власність скинута на root під час відновлення; директорії непрохідні для веб‑користувача (Завдання 6–8).
Виправлення: Виправте власність і права; розгляньте використання tar з збереженням власності і перевірте runtime‑користувача.
4) Випадкові дії плагінів 403, особливо на POST
Симптом: Збереження форм, оновлення редактора або API‑виклики падають з 403; GET працює.
Корінь: WAF/ModSecurity правило спрацювало на POST‑тіло або розмір (Завдання 12), або обмеження тіла запиту.
Виправлення: Знайдіть rule ID і налаштуйте/внесіть у whitelist вузько. Якщо причина в розмірі, підкоригуйте обмеження тіла в Nginx/Apache і PHP, але зберігайте розумні межі.
5) 403 для директорій, що мають бути публічними
Симптом: Відвідини /wp-content/ або інших директорій повертають 403.
Корінь: Відображення каталогів вимкнено — це очікувано — ви тестуєте директорію, а не файл; або відсутній index‑файл.
Виправлення: Запросіть фактичний файл (/wp-content/uploads/...). Переконайтеся, що index‑файли є там, де потрібно. Не вмикайте listing у продакшені, якщо не любите сюрпризи.
6) 403 з’являється після ввімкнення SELinux
Симптом: Права виглядають коректно, але все одно 403 з «permission denied» в логах або тихі блоки.
Корінь: Неправильні SELinux‑контексти (Завдання 10).
Виправлення: Відновіть правильні контексти (Завдання 11). Залишайте SELinux; виправляйте мітки, а не вимикайте захисні механізми.
Контрольні списки / покроковий план
Чеклист A: Триаж за 10 хвилин
- Захопіть заголовки через
curl -Iі помітьте маркери server/WAF (Завдання 1). - Тейліть access‑логи origin під час відтворення (Завдання 2).
- Обійдіть CDN з
--resolve, якщо можливо (Завдання 3). - Якщо origin бачить запит: перевірте error‑логи на «permission denied» або «access forbidden» (Завдання 4).
- Пошукайте deny‑правила в конфігах веб‑сервера для шляху (Завдання 5, Завдання 18).
- Перевірте проходження файловою системою за допомогою
namei -l(Завдання 6). - Якщо ще дивно: перевірте SELinux (Завдання 10) та логи WAF/ModSecurity (Завдання 12).
Чеклист B: Загартування без самосаботажу
- Блокуйте wp-admin по IP лише якщо маєте стабільні IP; інакше використовуйте SSO/VPN замість крихких allowlist.
- Лімітуйте логіни і XML‑RPC на edge. Віддавайте перевагу rate limits замість blanket denies, якщо ви справді не потребуєте функції.
- Документуйте кожне deny‑правило з коментарем і номером таска в конфігах. Майбутній ви — чужа людина; поводьтеся доброзичливо.
- Тримайте break‑glass методи: можливість обійти CDN до origin і можливість вимкнути плагіни через файлову систему.
- Моніторте false positive WAF: підраховуйте блокування за rule ID і URI, щоб можна було тонко налаштовувати з доказами.
Чеклист C: Валідація після виправлення
- Перевірте точно той URL, метод і заголовки, що падали (GET vs POST має значення).
- Підтвердіть, що логи тепер показують запит зі статусом 200/302 як очікувалося.
- Переконайтеся, що не внесли широкого розкриття (немає нового публічного доступу до
wp-config.php, немає listing каталогів). - Протестуйте з кількох мереж (офіс, LTE, зовнішня), щоб зловити політики за IP.
- Додайте синтетичну перевірку для ендпоїнта, що зламався (wp-login, wp-json, uploads‑файл).
Три корпоративні міні-історії з реального життя
Міні‑історія 1: Інцидент, спричинений неправильною припущенням
Компанія мала маркетинговий сайт на WordPress за CDN/WAF. Одного ранку редактори повідомили, що /wp-admin/ повертає 403. Інженери зробили те, що роблять під тиском: припустили «права» і почали змінювати власність і біти режимів у всьому дереві WordPress.
Це не допомогло. Ба більше — погіршило ситуацію: поспішний chown -R змінив власність директорії, якою очікував володіти деплой‑джоб, і тепер деплоями почали відмовляти. Дві команди, один 403 і зростаюче відчуття, що інтернет вирішив вас принизити.
Справжня причина була видна у першому захопленні заголовків curl: 403 обслуговував edge, і origin ніколи не бачив запит. Нове кероване правило WAF почало блокувати /wp-admin/ із «недовірених» країн, а редактори були в корпоративному VPN з виходом з іншої локації. З перспективи WAF‑а, адмінський трафік телепортувався.
Коли хтось обійшов CDN, враження було, що wp-admin працює. Виправлення — вузький виняток WAF для автентифікованих адмінських ендпоїнтів з діапазону egress VPN, плюс політика: зміни egress VPN без повідомлення операторам адмін‑доступу не допускаються.
Урок: якщо ви не знаєте, який шар породжує 403, ви ще не займаєтесь триажем — ви вгадуєте з правами root.
Міні‑історія 2: Оптимізація, що дала відкат
Інша команда хотіла швидше працювати та менше навантажувати origin. Вони ввімкнули агресивне кешування і бот‑захист на edge, включно з правилами «блокувати підозрілі POST‑запити» і «challenge для невідомих UA». Головна стрінка полетіла. Оцінки Lighthouse покращилися. Всі потиснули руки.
Потім плагін форм почав падати. Відправлення форм іноді повертали 403. Це траплялося лише для деяких користувачів і не відтворювалося стабільно в офісі. Класика.
Невдало: edge вважав деякі мобільні комбінації UA «низької репутації» і застосовував додаткову перевірку. Payload форми мав бінарний blob серіалізованих даних, що виглядав як сигнатура атаки для WAF. Деякі запити отримували challenge; інші — блокувалися.
Команда спочатку намагалася «оптимізувати» ще раз, додаючи нові правила кешування, що лише збільшило варіативність. Вони врешті витягли аудит‑події WAF по rule ID і зіставили з падіннями на ендпоїнті. Додали scoped‑виключення: дозволити POST для того endpoint з нормальною інспекцією, без агресивного порога. Конверсії відновилися.
Урок: оптимізації, які змінюють обробку запитів (особливо POST і автентифікаційні потоки), — це зміни надійності. Підходьте до них як до релізів з моніторингом та можливістю відкату, а не як до чекбоксу.
Міні‑історія 3: Нудна, але правильна практика, що врятувала день
Велике підприємство тримало кілька інстансів WordPress — частина legacy, частина сучасні, все за фермою зворотних проксі. У них була дуже непрезентабельна звичка: коли траплявся 403, інженер на чергуванні додавав до тікета три артефакти: заголовки від curl -I, рядок з access‑логу origin (або доказ, що його немає) і витяг з relevant error‑логу.
Однієї вихідної хвилі 403 вдарило по кількох сайтах, переважно на /wp-json/ і /wp-admin/admin-ajax.php. Миттєво підозрювали «оновлення WordPress». Але їхні три артефакти сказали інше: origin майже нічого не бачив, а сторінки 403 мали однакові edge‑ідентифікатори.
Оскільки вони мали послідовні докази, вони ескалували з мережевою/безпековою командою з точними таймштампами, request ID і шляхами. Security знайшли новий керований набір правил, що виявився надто агресивним для REST‑подібних endpoint‑ів. Вони тонко налаштували його, замість того, щоб вирізати WAF.
За годину інцидент локалізували. Не було панічних змін прав, перевмикання плагінів і «вимкнути безпеку» героїки опівночі.
Урок: нудний збір доказів — це мультиплікатор сили. Він не дозволяє вам боротися з невірним шаром і дає іншим командам конкретику, щоб допомогти швидко.
Питання та відповіді
Чому WordPress показує 403 лише на wp-admin?
Бо wp-admin — частий об’єкт загартування. CDN/WAF‑шаблони, серверні снипети і плагіни безпеки часто обмежують його по IP, країні, бот‑скору або rate limit. Підтвердіть, чи origin бачить запит; потім перевірте явні deny‑правила і системи банів.
Чи 403 завжди означає проблему з правами на диску?
Ні. Відмова файлової системи — лише одна поширена причина. Edge‑політики, правила Nginx/Apache, ModSecurity і плагіни також можуть повертати 403. Дивіться заголовки і логи, щоб спочатку встановити шар.
Які файлові права повинні бути у WordPress?
Типовий базовий набір: директорії 755, файли 644, власник — користувач/група, під якими працює веб‑сервер (або користувач деплою з груповим доступом). Директорії з записом зазвичай wp-content/uploads і можливо кеш‑директорії. Уникайте 777.
Чому я бачу 403, коли запитую директорію на кшталт /wp-content/?
Бо відображення каталогів зазвичай вимкнено. Якщо індекс‑файлу немає, сервер може повернути 403, щоб уникнути листингу вмісту. Тестуйте фактичний файл, а не директорію.
Як зрозуміти, чи мене блокує Cloudflare (або інший CDN)?
Перевірте заголовки (Завдання 1), потім підгляньте логи origin (Завдання 2). Якщо origin ніколи не бачить запит, а заголовки вказують на ідентифікатори edge, це edge‑блок. Підтвердіть, обійшовши CDN за допомогою --resolve (Завдання 3).
Чому сайт працює в браузері, а curl отримує 403?
Бот‑менеджмент і WAF часто підозрюють невідомі user‑agent. Спробуйте curl -I -A "Mozilla/5.0" для порівняння, але не «виправляйте», дозволяючи все — налаштуйте політику bot/WAF правильно.
Чи може плагін WordPress повернути 403, навіть якщо Nginx/Apache у порядку?
Так. Плагіни безпеки і кастомний код можуть відхиляти запити за IP, заголовками, шляхами або підозрою на атаку. Вимкніть плагіни через файлову систему для A/B‑тесту (Завдання 16). Якщо це вирішить проблему — увімкніть поетапно і знайдіть винуватця.
Чому POST‑запити падають з 403, а GET працює?
POST‑тіла інспектують WAF і вони можуть спрацювати на правила; також діють обмеження розміру і вмісту. Перевірте ModSecurity audit‑логи (Завдання 12) і серверні обмеження розміру запиту. Налаштовуйте вузько і переконайтеся, що не приховуєте реальну атаку.
Чи слід відключити WAF, щоб «довести», що це він?
Краще тестуйте обхід (пряме попадання в origin через resolve) або scoped‑обхід правила для одного шляху/IP. Повне відключення — крайній захід, тимчасовий і бажано в контрольованому дослідженні з моніторингом. Інакше ви «виправите» 403, запросивши гірші проблеми.
Висновок: наступні кроки, що працюють
403 — це проблема координації, що маскується під веб‑помилку. Виправлення рідко героїчне; зазвичай точне.
- Атрибутуйте 403 до шару за допомогою заголовків, логів origin і тестування обходу.
- Користуйтеся доказами: error‑логи для відмов файлової системи, WAF‑логи для rule ID, списки банів для IP‑блокувань.
- Застосовуйте найвужче виправлення: змінюйте одне правило, один шлях, одну контекстну мітку, одне піддерево директорій — і потім тестуйте.
- Запишіть, що змінили і додайте синтетичну перевірку для ендпоїнта, що зламався, щоб наступний 403 швидко ловився й діагностувався.