REST API WordPress заблоковано плагіном безпеки: дозволяйте безпечно, а не сліпо

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

Ваш сайт у браузері виглядає нормально, але мобільний додаток не може зайти, Gutenberg відмовляється завантажувати блоки або ваш headless‑фронтенд застряг на привітному повідомленні «Something went wrong».
Ви відкриваєте DevTools і ось він: /wp-json/ повертає 401, 403 або порожнє тіло з брендингом плагіна безпеки.

Спокусливе рішення — «просто відключити фаєрвол» або «дозволити REST API скрізь». Так ви перетворюєте баг продукту на інцидент безпеки.
Ми діагностуємо реальний блок, доведемо, що саме відбувається, і потім дозволимо тільки те, що має бути дозволено — безпечно й повторювано.

Що насправді робить REST API WordPress (і чому блоки заважають)

REST API WordPress знаходиться під /wp-json/. Це не просто «API для розробників». Це частина того, як працює сучасний WordPress.
Gutenberg, блочний редактор, покладається на нього. Багато плагінів залежать від нього. Деякі теми — теж. Headless‑настроювання потребують його як легені кисню.

Плагіни безпеки часто трактують /wp-json/ як «поверхню атаки», бо, чесно кажучи, це так.
REST‑ендпоїнти можуть виставляти дані, приймати записи й запускати дорогі запити.
Правильна безпекова позиція — не «блокувати все» і не «дозволити все». Це «забезпечити доступ відповідно до наміру».

Що зазвичай означає «заблоковано»

Коли хтось каже, що REST API заблоковано, мають на увазі одне з наступного:

  • 403 Forbidden: правило WAF, плагін безпеки, ModSecurity, ACL проксі чи CDN відмовляє в запиті.
  • 401 Unauthorized: REST‑ендпоїнт вимагає автентифікації; ваш клієнт не подає валідні облікові дані (або куки видаляються).
  • 404 Not Found: правила перезапису, маршрутизація або конфігурація сервера не дають WordPress отримати запит.
  • 5xx: запит доходить до WordPress, але спричиняє фатальну помилку, тайм‑аут або перевищення ресурсів.
  • 200 але неправильно тіло: ви отримуєте HTML (сторінка входу, блок‑сторінка) замість JSON — класичне «блоковано, але ввічливо».

Хороші операції починаються з класифікації. Якщо ви не можете сказати, де відбувається блок — на краю/CDN, у WAF/плагіні, у веб‑сервері, PHP або WordPress — ви просто впевнено здогадуєтесь.
Це не інженерія; це азартні ігри у худі.

Цікаві факти та історія, що пояснюють сучасні збої

Декілька контекстних моментів допоможуть передбачити, що ламається і чому інструменти безпеки насторожено ставляться до /wp-json/.

  1. REST API починався як фіч‑плагін. Він не завжди був в ядрі; дозрів перед злиттям у сам WordPress.
  2. WordPress 4.7 додав REST API у ядро. Тоді «проблеми з REST API» перестали бути нішевими, бо більше сайтів раптом на нього опиралися.
  3. Gutenberg збільшив обсяг викликів REST API. Редагування поста стало балакучою взаємодією клієнт‑сервер; фаєрволи, які «допускали кілька запитів», тепер бачили десятки.
  4. XML‑RPC був попереднім інтерфейсом для віддаленого доступу. Багато інструментів безпеки навчились боятися поверхонь віддаленої публікації через роки брутфорсу й зловживань pingback — REST успадкував підозру.
  5. Деякі ендпоїнти публічні за дизайном. Наприклад, ендпоїнти‑дискавері й багато операцій читання навмисно доступні; їх блокування ламає легітимну поведінку.
  6. WordPress має вбудовану модель автентифікації на основі куків для REST. Вона добре працює в браузерах, але headless‑додатки й сервер‑сервер клієнти часто потребують application passwords або OAuth‑подібних схем.
  7. Плагіни безпеки часто роблять патерн‑матчинг запитів. Вони не вивчають ваші наміри; вони використовують евристику: незвичні User‑Agent, сплески трафіку, JSON‑payload та ключові слова.
  8. Багато правил WAF були написані для загальних PHP‑додатків. Маршрути REST WordPress можуть виглядати як «path traversal» або «сканер» для правил, спроєктованих для інших аплікацій.
  9. Кешуючі шари можуть брехати. Кешована блок‑сторінка, яка віддає 200, все одно є блокуванням; вона просто витрачає більше вашого часу спочатку.

Корисна ментальна модель: інструменти безпеки не злі; вони просто надто буквальні.
Ваше завдання — дати їм вузько визначене виключення, яке вони зможуть зрозуміти, і логи, що доводять його ефективність.

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

Якщо ви на виклику, у вас немає часу на філософію. Ось послідовність «знайдіть вузьке місце швидко», що працює в реальному середовищі.

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

  • Чи містить відповідь заголовки CDN/WAF? Якщо так, починайте з краю.
  • Чи видно заголовки вашого веб‑сервера (nginx/Apache) і звичне JSON‑тіло? Тоді це рівень WordPress/додатка.
  • Чи бачите HTML з блок‑сторінкою? Тоді майже завжди це edge/WAF/плагін.

Друге: порівняйте неавторизовані й авторизовані запити

  • Неавторизований запит до /wp-json/ зазвичай має повертати JSON (індекс сайту) з 200.
  • Авторизовані запити повинні повертати дані; якщо вони падають — зосередьтесь на куках, nonce, application passwords або заблокованих заголовках.

Третє: підтвердьте, чи блоки базуються на правилах, лімітах або репутації

  • Rule‑based: послідовний 403 для конкретного шляху/payload.
  • Rate‑based: працює кілька запитів, потім падає; часто повертає 429/403.
  • Reputation‑based: працює з вашого ноутбука, але падає з CI/CD, регіону хмари або діапазону IP партнера.

Четверте: підтвердьте, що WordPress реально отримує запити

  • Перевірте access‑логи й зіставте з ID запитів або мітками часу.
  • Немає запису в access‑лозі? Блок стався вище в стеку.

Перефразована ідея (приписується): Вернер Фогельс пов’язаний з думкою, що «усе ламається постійно», тож ви проектуєте й налагоджуєте з цією очікуваністю.
Такий підхід — весь секрет тут: припускайте, що блоки шаруваті й переривчасті, поки не доведено протилежне.

Знайте ворога: де насправді відбувається блокування REST API

Рівень 0: DNS і неправильний хост

Ви будете здивовані, як часто «REST API заблоковано» — насправді «клієнт викликає інший хост, що вказує на інший стек».
Особливо в корпоративних налаштуваннях, де маркетинг має три домени, два CDN і staging, що вирвався в продакшен.

Рівень 1: CDN / edge WAF

Хмарні WAF чудово блокують сканери, але також чудово блокують ваші власні додатки, якщо ваші патерни схожі на сканерські.
REST‑ендпоїнти WordPress часто спрацьовують на сигнатури «зловживання API» або «PHP‑вразливість», бо URL містить передбачувані іменники й трафік буває сплесковим.

Рівень 2: правила зворотного проксі

Правила Nginx/Apache, що мають захищати xmlrpc.php, іноді випадково збігаються з wp-json.
Або хтось скопіював фрагмент, що блокує «усе, що не є переглядом сторінки», бо воював з ботами о 2 ранку.
Вони виграли. Ви програли.

Рівень 3: ModSecurity / CRS

OWASP Core Rule Set може позначити JSON‑payload, певні імена параметрів або кодування символів.
Якщо ви бачите ModSecurity в логах, ставтесь до нього як до окремої системи з власною політикою, а не як «частини Apache».

Рівень 4: плагін безпеки WordPress

Плагіни як Wordfence, iThemes Security, Sucuri (плагінна частина) тощо можуть блокувати через:

  • Блокування країн
  • Ліміти швидкості
  • Евристики «блокувати підозрілі URL»
  • Блокування «невідомих User‑Agent»
  • Блокування певних ендпоїнтів або шаблонів запитів

Біль у тому, що плагіни часто віддають загальну 403‑сторінку, яка виглядає ідентично кільком іншим шарам.
Потрібні логи, щоб знати, чи саме плагін це зробив.

Рівень 5: автентифікація та можливості WordPress

Не все «заблоковано» — це драма плагіна безпеки. Іноді REST працює, але ваш клієнт не авторизований.
Це — фіча. Баг — ваша помилка в припущенні.

Рівень 6: падіння PHP та ліміти ресурсів

REST‑запит може бути важчим за звичайний перегляд сторінки, бо він може запускати кастомні запити, завантажувати більше плагінів і обходити кеші.
Якщо плагін безпеки підвищує навантаження на CPU (деякі так роблять), API стає канаркою в шахті.

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

Ці завдання написані для типового Linux VM, що хостить WordPress за nginx або Apache, можливо з CDN.
Підлаштовуйте шляхи під ваш дистрибутив. Суть — метод: виміряти, класифікувати, а потім змінювати по одному.

Завдання 1: Отримайте індекс REST і перевірте заголовки

cr0x@server:~$ curl -sS -D - -o /dev/null https://example.com/wp-json/
HTTP/2 403
date: Sat, 27 Dec 2025 10:12:11 GMT
content-type: text/html; charset=UTF-8
server: cloudflare
cf-ray: 88c0c0d00abc1234-LHR

Що це означає: Блок на краю (Cloudflare в заголовках), а не WordPress.
Рішення: Припиніть змінювати налаштування WordPress. Спочатку дивіться логи та правила CDN/WAF.

Завдання 2: Порівняйте з origin (обхід CDN), щоб ізолювати шар

cr0x@server:~$ curl -sS -D - -o /dev/null --resolve example.com:443:203.0.113.10 https://example.com/wp-json/
HTTP/2 200
date: Sat, 27 Dec 2025 10:12:20 GMT
content-type: application/json; charset=UTF-8
server: nginx

Що це означає: Origin коректно віддає REST; CDN блокує.
Рішення: Реалізуйте вузьке правило дозволу WAF для /wp-json/ або конкретних маршрутів, а не глобальний обхід.

Завдання 3: Підтвердьте, чи WordPress взагалі отримує заблоковані запити

cr0x@server:~$ sudo tail -n 20 /var/log/nginx/access.log
203.0.113.50 - - [27/Dec/2025:10:12:20 +0000] "GET /wp-json/ HTTP/2.0" 200 2456 "-" "curl/8.5.0"

Що це означає: Ви бачите лише обхідний запит (200). Заблокований запит ніколи не дістався nginx.
Рішення: Ігноруйте PHP/WordPress‑логи для випадку 403; блок відбувається вище.

Завдання 4: Якщо підозрюєте плагін безпеки, перевірте логи плагіна на блокування

cr0x@server:~$ sudo grep -R "wp-json" -n /var/www/html/wp-content/wflogs 2>/dev/null | head
/var/www/html/wp-content/wflogs/attack-data.php:112:... "uri":"/wp-json/wp/v2/users", "action":"blocked" ...

Що це означає: Логи у стилі Wordfence показують URI і дію.
Рішення: Налаштуйте правило плагіна, що спрацювало, або створіть allowlist для конкретних ендпоїнтів і акторів.

Завдання 5: Доведіть, що ваш клієнт викликає правильні маршрути (маршрути мають значення)

cr0x@server:~$ curl -sS -i https://example.com/wp-json/wp/v2/posts?per_page=1 | sed -n '1,15p'
HTTP/2 200
content-type: application/json; charset=UTF-8
x-wp-total: 125
x-wp-totalpages: 125

Що це означає: Базові маршрути читання працюють.
Рішення: Якщо падають тільки певні маршрути (часто /users, /media, кастомні маршрути), звужуйте allow‑правила до цих маршрутів.

Завдання 6: Перевірте, чи відповідь — JSON або «гарна» HTML‑сторінка блоку

cr0x@server:~$ curl -sS -i https://example.com/wp-json/wp/v2/users | sed -n '1,25p'
HTTP/2 403
content-type: text/html; charset=UTF-8

<html><head><title>Access denied</title>...

Що це означає: Це не JSON від WordPress; це блок‑сторінка.
Рішення: Ще не гоніться за правами WP. Визначте, який шар віддає HTML.

Завдання 7: Перевірте, чи не зламалися правила перезапису WordPress (404, що маскується під «заблоковано»)

cr0x@server:~$ curl -sS -o /dev/null -D - https://example.com/index.php?rest_route=/ | head -n 10
HTTP/2 200
content-type: application/json; charset=UTF-8

Що це означає: Навіть якщо красиві permalink зламані, REST може працювати через rest_route.
Рішення: Якщо /wp-json/ падає, а rest_route працює, у вас проблема з перезаписом/проксі, а не з безпекою.

Завдання 8: Перевірте модуль ModSecurity на хіти правил, пов’язані з REST

cr0x@server:~$ sudo grep -R "wp-json" -n /var/log/modsec_audit.log | tail -n 3
--9c7d3a1f-H--
Message: Access denied with code 403 (phase 2). Matched phrase "wp-json" at REQUEST_URI.
Action: Intercepted (rule id: 949110)

Що це означає: Спрацьовує конкретне правило CRS.
Рішення: Не відключайте ModSecurity глобально. Виключте правило для конкретного місця або маршруту й задокументуйте зміни.

Завдання 9: Підтвердіть, чи це ліміт по швидкості, чи постійне блокування (burst тест)

cr0x@server:~$ for i in $(seq 1 20); do curl -sS -o /dev/null -w "%{http_code}\n" https://example.com/wp-json/; done | sort | uniq -c
     15 200
      5 429

Що це означає: У вас ліміт швидкості (429). Часто так роблять плагіни безпеки або CDN.
Рішення: Підніміть пороги для довірених акторів, додайте кешування для безпечних ендпоїнтів або сповільніть клієнта. Не «дозволяйте все».

Завдання 10: Інспект конфіг nginx/Apache на предмет випадкових блокувань

cr0x@server:~$ sudo nginx -T 2>/dev/null | grep -n "wp-json\|rest_route\|deny\|return 403" | head -n 20
1321:    location ~* /(xmlrpc\.php|wp-json) { return 403; }

Що це означає: Хтось заблокував wp-json на рівні веб‑сервера (часто скопійовано з «анти‑бот» фрагмента).
Рішення: Видаліть wp-json з цього блоку або замініть його на точніше правило (наприклад, дозволити індекс/читання, обмежити записи).

Завдання 11: Перевірте, що сам WordPress не вимкнув REST API

cr0x@server:~$ wp --path=/var/www/html plugin list --status=active
+-----------------------+--------+-----------+---------+
| name                  | status | update    | version |
+-----------------------+--------+-----------+---------+
| disable-json-api      | active | available | 1.3.0   |
| wordfence             | active | none      | 7.11.0  |
| classic-editor        | active | none      | 1.6.3   |
+-----------------------+--------+-----------+---------+

Що це означає: Плагін явно вимикає JSON/REST‑функціональність.
Рішення: Вирішіть, чи цей плагін відповідає вашій архітектурі. Якщо вам потрібен REST для Gutenberg/headless — він за дизайном несумісний.

Завдання 12: Перевірте, що автентифікація на рівні додатка працює (Application Passwords)

cr0x@server:~$ curl -sS -u "api-user:abcd efgh ijkl mnop" -o /dev/null -D - https://example.com/wp-json/wp/v2/users/me | head -n 12
HTTP/2 200
content-type: application/json; charset=UTF-8

Що це означає: Автентифікація валідна і не відтинається проксі/WAF.
Рішення: Якщо це падає з 401/403 — перевірте, чи Basic Auth не блокується вище, або чи користувач має потрібні права.

Завдання 13: Доведіть, що куки/nonce не спотворюються на краю

cr0x@server:~$ curl -sS -I https://example.com/wp-json/ | grep -i "set-cookie\|cache-control\|vary"
cache-control: no-cache, must-revalidate, max-age=0
vary: Accept-Encoding

Що це означає: Індекс REST не повинен агресивно кешуватися з куками. Якщо бачите дивну поведінку з куками — плагін може інжектувати куки скрізь.
Рішення: Виправте інжекцію куків і політику кешування; інакше CDN може закешувати «заблоковані» відповіді.

Завдання 14: Корелюйте час із PHP‑FPM або насиченням бекенда (коли це «блок», але насправді повільно)

cr0x@server:~$ sudo tail -n 20 /var/log/php8.2-fpm.log
[27-Dec-2025 10:12:44] WARNING: [pool www] server reached pm.max_children setting (20), consider raising it

Що це означає: Запити ставлять у чергу; WAF може почати тайм‑аутити і повертати синтетичні 403/524‑подібні помилки залежно від провайдера.
Рішення: Виправте ємність і повільні запити першочергово; інакше ви «дозволите» REST API і все одно будете падати.

Завдання 15: Перевірте, чи ваші REST‑маршрути кешуються неправильно (відчуття отруєння кеша)

cr0x@server:~$ curl -sS -I https://example.com/wp-json/wp/v2/posts?per_page=1 | egrep -i "cache|age|cf-cache-status|x-cache|via"
cf-cache-status: HIT
age: 1800

Що це означає: REST‑відповідь кешується на краю. Це добре для публічних читань, катастрофа для персоніфікованих маршрутів.
Рішення: Кешуйте тільки безпечні публічні GET‑маршрути; пропускайте кеш для автентифікованих або маршрутів запису.

Жарт #1: Плагіни безпеки як аеропортна перевірка — в цілому добре, поки ви не той, у кого ноутбук і ремінь. Тоді стає особисто.

Дозволяти безпечно: патерни, що не зіпсують ваш вікенд

Правило №1: Не «увімкнути REST API». Він уже увімкнений. Визначте, хто що може робити.

REST API — це не один перемикач. Це фреймворк маршрутизації з ендпоїнтами, методами й правами.
Безпечний шлях — дозволити потрібні ендпоїнти та методи для конкретних акторів, з логуванням і лімітами.

Почніть з інвентарю: хто викликає ваш API?

Перш ніж змінювати правила, перелічіть клієнтів:

  • Редактор Gutenberg у wp‑admin (браузерні клієнти, cookie‑автентифікація, nonce)
  • Мобільні додатки (зазвичай використовують application passwords або OAuth‑флоу)
  • Headless‑фронтенд (сервер‑сервер або браузер‑сервер, іноді обидва)
  • Інтеграції партнерів (системи на кшталт Zapier, маркетинг‑автоматизація, CRM)
  • Внутрішні cron/воркери (імпортери, синхронізатори)

Кожен має різні схеми автентифікації та патерни швидкості. Якщо ви третуєте їх як одне ціле, WAF теж так їх сприйматиме.
І він заблокує «байдужу масу».

Патерн A: Дозволити публічні маршрути читання, захистити решту

Багатьом сайтам потрібен лише неавторизований GET‑доступ до публічних ендпоїнтів (пости, сторінки, категорії, метадані медіа).
У такому випадку:

  • Дозволяйте GET для відомих публічних маршрутів (наприклад, /wp-json/wp/v2/posts)
  • Блокуйте або кидайте challenge для не‑GET методів від анонімних клієнтів
  • Тримайте маршрути, що вимагають автентифікації, доступними, але за належної автентифікації

Це відповідає принципу найменших привілеїв і знижує сигнатури «зловживання API», бо ви не дозволяєте повний набір методів випадковому трафіку.

Патерн B: Дозвольте індекс REST, але не ендпоїнти для переліку

Індекс REST (/wp-json/) часто використовується клієнтами для discovery‑метаданих.
Його блокування може зламати Gutenberg і UI плагінів. Але ви все ще можете обмежити чутливі ендпоїнти:

  • Дозвольте /wp-json/ і /wp-json/wp/v2 для повернення метаданих індексу
  • Ускладніть ендпоїнти, що витікають дані (наприклад, перелік користувачів), через права або WAF‑правила

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

Патерн C: Для headless‑додатків використовуйте application passwords і виділених користувачів

Application Passwords практичні для сервер‑сервер викликів. Безпечна модель виглядає так:

  • Створіть виділеного користувача WordPress для інтеграції
  • Надайте мінімальну роль/можливості, необхідні для задачі
  • Видайте application password, обертайте його і зберігайте в менеджері секретів
  • Додайте в білый список IP інтеграції на WAF, якщо можливо (але не покладайтеся лише на це)

Уникайте використання admin‑акаунта «бо так працює». Воно завжди працює. Ось у чому проблема.

Патерн D: Використовуйте селективні винятки WAF, а не глобальні обходи

Хороше виняток WAF має:

  • Конкретний матч шляху або маршруту (не wildcard на /*)
  • Обмеження по методах (тільки GET, або дозволити POST лише для конкретного маршруту)
  • Обмеження по актору (IP, mTLS, заголовок‑токен або хоча б відомий User‑Agent, якщо вже дуже треба)
  • Тимчасове вікно для змін в аварійному режимі (потім зробити постійно правильно)
  • Логування: «це виняток використовували чи ні»

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

Патерн E: Рухи запису трактуйте як продакшн‑зміни

Дозвіл POST, PUT, PATCH, DELETE до REST‑ендпоїнтів — це не «зробити працювати додаток».
Це дає віддаленим клієнтам можливість змінювати стан. Потрібні:

  • Управління змінами (так, навіть якщо ви «agile»)
  • Аудит‑логи (хто і що змінив через API)
  • Ліміти швидкості і детекція зловживань
  • Резервні копії і шляхи відкату

Патерн F: Кешуйте тільки те, що безпечно кешувати

REST‑ендпоїнти іноді чудово підходять для кешування (публічний контент), а іноді — катастрофа (усе, що залежить від користувача).
На краю:

  • Кешуйте GET‑запити без заголовка Authorization і без куків
  • Пропускайте кеш, якщо існує заголовок Authorization
  • Пропускайте кеш для /wp-json/wp/v2/users/me, адміноподібних ендпоїнтів і кастомних автентифікованих маршрутів

Якщо плагін безпеки блокує API‑виклик і CDN кешує цю блок‑відповідь, ви створили машину відмови в обслуговуванні на автопілоті.
Це не гіпотетично; таке трапляється.

Чого уникати категорично

  • Відключення плагіна безпеки як «тест» і забуття увімкнути назад.
  • Дозвіл /wp-json/ глобально на WAF без обмежень по методам.
  • Дозволяти Basic Auth усюди без чіткого TLS‑термінування і контролю брутфорсу.
  • Використання admin API‑користувача для інтеграцій.
  • Ігнорування логів і налаштування за забобонами.

Жарт #2: «Тимчасовий allow‑all» — це операційний варіант «я тримаю стакан з водою над клавіатурою». Добре, поки ні.

Три корпоративні міні‑історії з практики

Інцидент через неправильне припущення: «403 означає права WordPress»

Середня компанія розгорнула headless маркетинговий сайт. Фронтенд був на сучасному фреймворку; WordPress — бекенд контенту.
У staging усе працювало, а в продакшені з’явилися 403 на /wp-json/wp/v2/posts.
Інженери припустили, що це проблема ролей WordPress, бо REST API — «це WordPress‑штука». Розумно, але неправильно.

Вони витратили години на налаштування ролей, додавання плагінів і навіть тимчасово дали адміністраторські права API‑користувачу.
403 лишався. Вони «вирішили» проблему, дозволивши весь трафік на /wp-json/ у плагіні безпеки — здавалося, працювало день.
Наступного ранку дашборд WAF показав сплеск заблокованих запитів по незв’язаних ендпоїнтах і хвилю автоматичних шпигунських сканів.

Корінь проблеми був вгорі: CDN WAF мав кероване правило, яке трактувало патерни REST як сканер API.
У staging був інший план з меншим набором правил. У проді — повний «enterprise protection».
Зміна плагіна була не по справі; вона просто змінила час, щоб збити всіх з пантелику.

Виправлення було нудним і точним: додати виняток WAF для GET на /wp-json/wp/v2/posts і /wp-json/, зберегти захист для всього іншого і додати ліміт швидкості.
Також додали синтетичну перевірку в моніторингу, що алертує про не‑200 відповіді для ключових REST‑маршрутів.
Інцидент закінчився, коли перестали звинувачувати додаток і довели, де генерується 403.

Оптимізація, що відкотилася: кешування REST API «щоб зменшити навантаження на origin»

Інша організація мала WordPress під великим трафіком. Хтось вирішив «кешувати REST API на CDN», бо фронтенд робив багато викликів.
Для публічних списків постів це було хорошою ідеєю. Для автентифікованих ендпоїнтів — повільна катастрофа.

Вони впровадили широке правило кешування для /wp-json/* і побачили падіння навантаження на origin. Усі аплодували.
Потім редактори почали скаржитись, що Gutenberg не може консистентно зберігати чернетки, а персоніфіковані дані були некоректні.
Деякі відповіді API були застарілими; деякі повертали HTML‑сторінки входу, закешовані як JSON; деякі були 403, закешовані на хвилини.

Інструменти безпеки загострилися. Плагін безпеки виявив повторювані «invalid nonce» патерни (бо клієнт отримував закешовані відповіді, що не відповідали сесії)
і почав блокувати запити. Тепер CDN кешував блок‑сторінки теж.
Система була «оптимізована» в самопідсилювану петлю збою: кешували неправильне → клієнт повторює → WAF блокує → кешує блок → більше повторів.

Відкат був негайний: перестали кешувати автентифіковані маршрути і пропустили кеш для запитів з Authorization або куками.
Кеш залишили для короткого allowlist публічних GET‑маршрутів з явними TTL.
Навантаження на origin трохи зросло, але платформа знову стала стабільною — бо коректність найкраще оптимізує продуктивність.

Нудна, але правильна практика, що врятувала день: журнали змін + кореляція запитів

Третя команда була не гучною. Вони керували WordPress для бізнес‑підрозділу, який не хотів сюрпризів.
У них була операційна звичка: кожна зміна правила WAF вимагала тикет, короткий опис і посилання на приклад логу, що показує, чому це потрібно.
Вони також інжектували request ID заголовок на краю і логували його на origin.

Одного дня REST‑виклики почали падати лише для одного партнера. Моніторинг піймав сплеск 403 для кастомного namespace.
Замість гадань вони шукали логи по request ID і побачили, що відповідь генерувалася на краю, а не WordPress.
Далі відкрили журнал змін WAF і знайшли оновлення керованого правила, що почало трактувати JSON‑payload партнера як підозрілий.

Через кореляційні ID вони могли надіслати точний зразок невдалого запиту (без чутливих полів) до секюріті‑команди й швидко отримати вузько спрямоване виняток.
Ніхто не відключав фаєрвол. Ніхто не «тимчасово дозволив усе». Ніхто не сварився в Slack три години.
Виправлення було розгорнуто, перевірено і задокументовано.

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

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

1) Симптом: /wp-json/ повертає 403 з HTML‑сторінкою

Корінна причина: Блок‑сторінка edge WAF або плагіна безпеки віддається, іноді закешована.

Виправлення: Визначте шар через заголовки; обхід CDN до origin для підтвердження; створіть вузьке правило дозволу для індексу /wp-json/ і потрібних маршрутів.

2) Симптом: Працює з вашого ноутбука, падає з CI/CD або сервера

Корінна причина: Репутація IP, геоблок, або різний User‑Agent тригерять евристику безпеки.

Виправлення: Використовуйте application passwords; allowlist IP‑діапазони CI, якщо стабільні; зменшіть частоту запитів; встановіть коректний User‑Agent; уникайте «curl default» у продакшн‑клієнтах.

3) Симптом: Редактор Gutenberg показує «The response is not a valid JSON response»

Корінна причина: REST‑відповідь була замінена HTML (сторінка входу, блок WAF, попередження PHP) або кеш повертає не‑JSON тіла.

Виправлення: Отримайте впавший ендпоїнт через curl -i і інспектуйте content‑type/тіло; виправте upstream‑блок; усуньте PHP‑попередження; переконайтесь, що REST‑маршрути не кешуються HTML‑тілами.

4) Симптом: 401 Unauthorized на ендпоїнтах, які «мають бути публічні»

Корінна причина: Плагін або кастомний код змінили вимоги автентифікації REST, або клієнт потрапляє на маршрут, що вимагає автентифікації (наприклад, users/me).

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

5) Симптом: Працює кілька запитів, потім 429/403

Корінна причина: Ліміти швидкості у плагіні безпеки або edge WAF; іноді спричинені бурстовою поведінкою Gutenberg.

Виправлення: Підніміть пороги для автентифікованого/редакторського трафіку; додайте кешування на рівні маршруту для публічних читань; налаштуйте поведінку повторів клієнта; уникайте retry‑штормів.

6) Симптом: REST працює на origin, але падає через CDN

Корінна причина: Керовані правила WAF або захист від ботів на CDN.

Виправлення: Додайте виняток для необхідних маршрутів/методів; пропустіть бот‑челенджі для автентифікованих адмін/редактор шляхів; збережіть захист для маршрутів запису.

7) Симптом: 404 на /wp-json/, але rest_route працює

Корінна причина: Правила перезапису або проксі не форвардять шлях правильно.

Виправлення: Виправте nginx/Apache rewrite правила; переконайтесь, що permalink‑и WordPress встановлені; перевірте location‑блоки, що перехоплюють /wp-json.

8) Симптом: Випадкові 5xx на REST‑ендпоїнтах, особливо кастомних

Корінна причина: Переповнення PHP‑FPM, повільні DB‑запити, фатальні помилки плагінів або накладні витрати плагіна безпеки під навантаженням.

Виправлення: Перевірте логи PHP‑FPM щодо max_children; перегляньте логи повільних запитів; профілюйте плагінні ендпоїнти; впровадьте кешування або збільшіть ресурси обережно.

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

Покроково: відновити функціональність без прошивання діри у вашому периметрі

  1. Захопіть зразок впалого запиту. Збережіть метод, URL, заголовки, код відповіді і фрагмент тіла.
  2. Класифікуйте код помилки. 401 vs 403 vs 404 vs 5xx диктує наступний крок.
  3. Перевірте заголовки відповіді на предмет origin vs edge. Якщо бачите CDN/WAF‑заголовки — починайте там.
  4. Обійдіть CDN для тесту origin. Використайте curl --resolve з довіреного хосту.
  5. Підтвердіть, чи WordPress логирує запит. Якщо немає запису в access‑лозі — блок upstream.
  6. Визначте точні ендпоїнти, що потрібні. Не «фіксуйте wp-json»; фіксуйте конкретні маршрути, які використовує ваш додаток.
  7. Вирішіть публічний чи автентифікований доступ. Публічні маршрути читання можна дозволити і кешувати; маршрути запису/автентифіковані — під захистом.
  8. Реалізуйте вузьке правило дозволу. Шлях + метод + обмеження по актору, де можливо.
  9. Додайте ліміти швидкості для REST‑маршрутів. Особливо для анонімного трафіку.
  10. Перевірте бурст‑тестом. Підтвердіть відсутність 429/403 при очікуваному навантаженні.
  11. Аудитуйте політику кешування. Переконайтесь, що автентифіковані запити не кешуються; блок‑сторінки не кешуються як 200.
  12. Задокументуйте зміну. Запишіть, що дозволено, чому і як відкотити.
  13. Додайте моніторинг. Синтетичні перевірки ключових REST‑ендпоїнтів і алерти на сплески 401/403/429/5xx.

Чек‑лист готовності до продакшену для дозволів REST API

  • Виділені API‑користувачі з мінімальними привілеями
  • Облікові дані в менеджері секретів і ротація
  • WAF‑винятки обмежені точними маршрутами й методами
  • Ліміти швидкості для анонімного REST‑трафіку
  • Політика кешування: лише публічні GET, пропуск при Authorization/куках
  • Логування: кореляція edge + origin, а також логи плагінів, якщо використовуються
  • Runbook: як визначити, який шар блокує
  • Моніторинг: health‑чек REST і алерти на помилки

FAQ

1) Чи варто відключати плагін безпеки, щоб підтвердити, що він проблема?

Тільки якщо ви можете зробити це безпечно і коротко (вікно технічного обслуговування, обмежений доступ), і тільки після перевірки заголовків/логів, щоб побачити, чи блок upstream.
У багатьох реальних конфігураціях CDN/WAF блокує ще до того, як WordPress бачить запит — відключення плагіна нічого не доведе.

2) Чи безпечно дозволяти /wp-json/?

Дозвіл індексу REST і конкретних публічних GET‑маршрутів зазвичай безпечний і часто необхідний. Дозволяти всі методи для всіх маршрутів — небезпечно.
Безпека полягає в обмеженні: маршрут, метод, актор і ліміти.

3) Чому Gutenberg ламається, коли REST API заблокований?

Gutenberg використовує REST‑ендпоїнти для отримання, автозбереження, валідації і керування блоками. Якщо /wp-json/ повертає HTML або 403/401, редактор не може працювати.
Повідомлення про помилку часто загальне, бо клієнт очікував JSON, а отримав щось інше.

4) У чому різниця між 401 і 403 для REST?

401 зазвичай означає «ви не автентифіковані (або облікові дані недійсні)». 403 часто означає «вам заборонено», або «WAF блокує вас».
На практиці плагіни безпеки й WAF часто використовують 403 для всього, тож треба перевіряти заголовки й логи.

5) Чи можна просто дозволити IP інтеграційного сервера і вважати проблему закритою?

Allowlist IP допомагає, але це не автентифікація. IP змінюються, існують NAT; зловмисники все ще можуть атакувати публічні ендпоїнти.
Використовуйте реальну автентифікацію (application passwords, токени) і розглядайте IP‑слід як додатковий шар, а не єдиний.

6) Мій REST API працює через rest_route, але не через /wp-json/. Що це означає?

Це вказує на проблеми перезапису/проксі, а не на «вимкнений» REST. Виправте nginx/Apache правила перезапису; переконайтесь, що permalink‑и налаштовані; перевірте форвардинг шляху проксі.

7) Чому мій WAF вважає REST‑запити атакою?

Бо багато атак використовують передбачувані ендпоїнти й автоматизацію. REST‑трафік часто буває сплесковим, містить JSON і звертається до маршрутів, що виглядають як перелік.
Рішення не в аргументі з WAF; воно в наданні вузьких винятків для легітимних клієнтів і збереженні захисту для решти.

8) Чи слід блокувати ендпоїнти користувачів, щоб запобігти enum‑перелікам?

Переконайтесь, що чутлива інформація про користувачів не доступна публічно. Сучасний WordPress вже багато чого обмежує.
Якщо додаєте WAF‑обмеження, робіть це точно: блокувати анонімний доступ до маршрутів для переліку користувачів, але не ламати автентифіковані адмін/редактор робочі процеси.

9) Чи може кешування REST‑ендпоїнтів безпечно покращити продуктивність?

Так — для публічних, неавтентифікованих GET‑маршрутів. Кешуйте обережно і пропускайте кеш для автентифікованого трафіку (Authorization/куки).
Ніколи не кешуйте відповіді на операції запису і слідкуйте, щоб не закешувати HTML‑сторінки блоку.

10) Що робити, якщо плагін безпеки блокує лише певні кастомні ендпоїнти?

Це часто трапляється. Кастомні ендпоїнти можуть мати незвичні payload або параметри, що виглядають підозріло.
Додайте винятки для конкретних маршрутів або підлаштуйте саме правило, що спрацьовує; не дозволяйте усі кастомні неймспейси без аудиту.

Висновок: наступні кроки, які можна впровадити сьогодні

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

Практичні кроки:

  1. Запустіть тести заголовків і обходу origin, щоб локалізувати шар блокування.
  2. Складіть список точних REST‑маршрутів, які потребує ваш додаток (читання vs запис).
  3. Створіть вузьке виняток у WAF/плагіні: шлях + метод + обмеження по актору.
  4. Перевірте правила кешування: кешуйте лише безпечні публічні GET‑маршрути; пропускайте при Authorization/куках.
  5. Додайте моніторинг для ключових REST‑ендпоїнтів і алерти на сплески 401/403/429/5xx.
  6. Задокументуйте виняток і заплануйте ревізію — винятки мають властивість ставати постійною інфраструктурою.
← Попередня
MySQL vs RDS MySQL: приховані обмеження, які б’ють під час інцидентів
Наступна →
Proxmox NFS «stale file handle»: чому це трапляється і як запобігти

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