Apache для WordPress: модулі й правила, що ламають сайти (та як це виправити)

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

Більшість відмов WordPress на Apache починаються не з WordPress. Вони починаються зі «ще однієї невеликої зміни Apache», яка здавалася безпечною:
нове правило безпеки, налаштування стиснення, «прибирання» редиректів або хитрий заголовок кешування, скопійований із запису 2014 року.
Потім ваша головна сторінка починає редиректити сама себе, адмінка перетворюється на 403‑експонат, або виклики REST API загадково відмовляють лише по вівторках.

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

Швидкий план діагностики

Коли WordPress «ламається» на Apache, найшвидший шлях — класифікувати збої за HTTP‑симптомом,
потім підтвердити відповідний рівень (Apache проти PHP‑FPM проти WordPress проти мережі/CDN).
Не здогадуйтеся. Змусьте сервер розказати правду.

Перш за все: ідентифікуйте клас помилки за 90 секунд

  1. Отримайте чисту відповідь з origin (обійдіть CDN, якщо він є). Використайте curl з verbose і слідкуванням редиректів.
    Ви шукаєте схеми коди статусів і ланцюжки редиректів.
  2. Перевірте логи помилок Apache на той самий час. Один рядок часто називає модуль‑винуватець (security2, rewrite, proxy_fcgi).
  3. Перегляньте поля access‑логу: статус, байти, час запиту, upstream‑час (якщо логовано), user agent.
    Якщо ви не логувати час запиту — почніть робити це сьогодні.

По‑друге: визначте, у який з чотирьох кошиків ви потрапили

  • Цикл редиректів / неправильний протокол / неправильний хост: майже завжди правила перезапису, заголовки проксі або невідповідність siteurl/home у WordPress.
  • 403 Forbidden: mod_security, права файлової системи або правила авторизації Apache (Require, AllowOverride, Options).
  • 500/502/503: неправильне підключення PHP‑хендлера (proxy_fcgi), невідповідність MPM/PHP, таймаути або обмеження пам’яті.
  • Повільно або нестабільно: KeepAlive, краєві випадки HTTP/2, стиснення, заголовки кешування, насичення бекенду, дискові I/O або DNS/проксі проблеми.

По‑третє: підтвердіть одним цілеспрямованим тестом

Оберіть тест, який руйнує невизначеність:
вимкніть модуль на staging vhost, обійдіть перезапис для одного шляху, запустіть один запит з інструментацією рівня RewriteLog,
або відтворіть запит безпосередньо до бекенду.

Одна операційна цитата, яку варто витатуїрувати в runbook, бо вона працює під тиском:
«Надія — це не стратегія.» — Gene Kranz

Цікаві факти й історичний контекст (корисне, не тривіальне)

  • .htaccess існує насамперед через шаред‑хостинг: він дозволяв користувачам керувати конфігом per‑directory без root‑доступу. Зручно, але дорого в рантаймі.
  • mod_php штовхнув Apache до prefork: роками запуск PHP всередині Apache означав, що prefork MPM був безпечним вибором. Багато хостів WordPress досі несуть цю спадщину.
  • «Pretty permalinks» у WordPress — це по суті правила перезапису: канонічні правила писалися спочатку для mod_rewrite, потім адаптувалися для інших середовищ. Apache — еталонна форма.
  • mod_security став масовим у 2000‑х: він потужний, і його помилкові спрацювання проти адмінки WordPress та XML‑RPC легендарні.
  • HTTP/2 в Apache розвивався поступово: ранні впровадження стикалися з сумісністю у клієнтів та проміжних елементів. Зараз переважно стабільно, але неправильні налаштування все ще кусають.
  • Brotli з’явився пізніше за gzip: mod_brotli новіший, і старі проксі/кеші можуть поводитися неправильно, якщо заголовки Vary не налаштовані.
  • Синтаксис authz в Apache змінився: ера «Order allow,deny» поступилася «Require all granted». Змішування версій або сліпе копіювання фрагментів викликає випадкові 403.
  • «AllowOverride None» був PSA щодо продуктивності: відключення сканування .htaccess покращує продуктивність, але зламає permalinks WordPress, якщо не перенести правила у vhost.

Типові рушії збоїв: модулі й правила, що шкодять WordPress

1) mod_rewrite: тихий автор ваших циклів редиректів

WordPress потребує правил перезапису для красивих посилань. Проблема не в самому mod_rewrite; проблема в людях.
Конкретно: люди, які змішують редиректи (301/302), примусове канонічне ім’я хоста, HTTP→HTTPS та видалення/додавання trailing slash
на кількох рівнях (Apache + WordPress + CDN + балансувальник навантаження).

Типові шаблони збоїв:

  • Нескінченний редирект на ту саму URL: два правила сперечаються про протокол або хост, або WordPress думає, що працює на HTTP, тоді як клієнти приходять через HTTPS.
  • Адмінка редиректить на головну: WordPress бачить інший хост/протокол, ніж браузер, через відсутність заголовків проксі.
  • REST API 404: правила перезапису не застосовуються до /wp-json, бо AllowOverride блокує .htaccess, або правило перериває обробку.

Практична порада: якщо у вас є root‑доступ, тримайте правила WordPress не в .htaccess, а у vhost. Менше файлових stat, менше сюрпризів.
Але якщо так робите — ставте це як код: версіонуйте, тестуйте й документуйте інваріанти (імена хостів, схеми й канонічні шляхи).

2) AllowOverride і Options: «працює на staging» — це не конфігурація

Найпоширеніша причина «перmalink‑и WordPress зламалися» на Apache — це зовсім не WordPress. Це Apache ігнорує правила.
Таке трапляється, коли ви ставите:

  • AllowOverride None (тому .htaccess ігнорують)
  • або перевизначаєте Options / Require так, що блокуєте доступ.

«Хороше» виправлення — дозволити ті типи override, які потрібні WordPress (зазвичай мінімум AllowOverride FileInfo),
або перенести правила перезапису у vhost і тримати AllowOverride None заради продуктивності.

3) mod_security (security2): захист, що часто блокує адмінку

mod_security — це веб‑аплікаційний фаєрвол. Він аналізує запити і блокує шаблони, що виглядають шкідливими.
Трафік адмінки WordPress виглядає підозрілим і без того: багато параметрів, серіалізовані дані, JSON‑блоби й плагіни з «креативними» query‑рядками.

Класичні випадки відмов:

  • 403 на wp-admin після встановлення плагіна або вмикнення page builder.
  • Запити REST API блокуються, що призводить до збоїв блочного редактора, поламаних embeds або AJAX‑помилок.
  • Випадкові логіни через те, що певні куки/заголовки тригерять правила.

Порада з практики: не вимикайте mod_security глобально. Налаштовуйте його на рівні vhost і по конкретних id правил, і ведіть аудиторський лог, який ви реально читаєте.
«Ми вимкнули WAF» — це короткострокове вирішення, яке стане довгостроковим інцидентом.

4) mod_proxy_fcgi та PHP‑FPM: 502, що виглядають як проблеми WordPress, але ні

Сучасні Apache + WordPress часто означають, що Apache віддає статичні ресурси, а PHP проксує на PHP‑FPM через proxy_fcgi.
Неправильні налаштування тут дають:

  • 502 Bad Gateway (FPM впав, права сокета, невірний шлях, таймаут)
  • 503 Service Unavailable (бекенд насичений, досягнуто max children)
  • Переривчасті 500 (крахи, виснаження пам’яті, повільні запити потрапляють на таймаути)

5) Вибір MPM (event/worker/prefork): тонке налаштування, що ламається під навантаженням

MPM Apache вирішує, як обробляються з’єднання.
Сам WordPress не переймається, але ваша інтеграція PHP і форма трафіку — так.

  • prefork: старий процес‑на‑з’єднання; працює з mod_php; витратний з пам’яттю.
  • worker: потоки; краща конкурентність; вимагає потокобезпечних модулів.
  • event: найкращий загальний вибір для keep‑alive‑важкого трафіку; добре поєднується з PHP‑FPM.

Збої виникають через невідповідність: ввімкнули event MPM, але все ще завантажується mod_php, або встановили агресивні MaxRequestWorkers без запасу пам’яті.
WordPress стає козлом відпущення, тоді як Apache тихо вбиває робочі процеси через OOM.

6) HTTP/2 (mod_http2): добре, доки проксі й заголовки налаштовані вірно

HTTP/2 зазвичай допомагає фронтендам WordPress мультиплексуванням запитів. Але неправильні налаштування дають дивні відмови:

  • Деякі клієнти зависають через проміжні проксі або багаті TLS‑налаштування.
  • Різке підвищення CPU з певними наборами шифрів і налаштуваннями стиснення.
  • Server push (якщо використовується) може дратувати кеші й марнувати трафік. Більшість сайтів уже не повинні використовувати HTTP/2 push.

7) Модулі стиснення: mod_deflate проти mod_brotli і пастка Vary

Стиснення економить трафік. Але воно може створити отруєння кешу, якщо ваш шар кешування ігнорує переговори контенту.
Якщо ви вмикаєте brotli і gzip, треба правильно налаштувати заголовки відповіді, особливо Vary: Accept-Encoding.

Типова помилка: ввімкнули brotli для всього, а потім виявили, що upstream‑кеш віддає brotli‑стиснений CSS клієнту, який його не підтримує.
Сторінка стає модерним витвором мистецтва з поламаними стилями.

8) Заголовки кешування і mod_expires: «оптимізація», що заморожує тему в часі

Ви хочете довгі терміни кешування для статичних ресурсів. Ви не хочете довгих термінів кешування для HTML або динамічних endpoint‑ів.
Теми й плагіни WordPress постачають ресурси, що змінюються; якщо ви ставите агресивне кешування без версіонованих імен файлів, користувачі довго бачать старий JS.

9) mod_headers: заголовки безпеки, що ламають логіни

Заголовки безпеки важливі. Але ви можете зламати автентифікацію WordPress і embeds, якщо перестараєтеся:

  • SameSite для cookie з дивними налаштуваннями може ламати сторонні інтеграції.
  • Content-Security-Policy занадто сувора — ламає блочний редактор, embeds і аналітику.
  • X-Frame-Options блокує легітимне використання iframe (прев’ю, деякі SSO‑флоу), якщо не передбачити винятків.

10) DirectoryIndex, MultiViews і інші «дрібні» фічі, що викликають великі дивності

Функції Apache, що здаються не пов’язаними з WordPress, часто конфліктують з ним:

  • MultiViews (content negotiation): може ламати permalinks, відображаючи URL на несподівані файли. Якщо ви запускаєте WordPress — зазвичай вимикайте це.
  • DirectoryIndex неправильно налаштований: може викликати 403/404 там, де очікувався front controller WordPress.
  • Стикнення Alias і ProxyPass: один конфліктний шлях може поглинути /wp-admin або /wp-json.

Жарт №1: Apache може майже все. У цьому ж проблема — це можуть робити й ваші колеги.

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

Нижче — конкретні завдання, які ви можете виконати на хості Apache. Кожне містить, що означає вивід і яке рішення приймати.
Це не «приємні речі». Це найкоротший шлях від симптомів до причини.

Завдання 1: Підтвердіть реальний режим відмови на origin за допомогою curl

cr0x@server:~$ curl -svL -o /dev/null https://example.com/ 2>&1 | sed -n '1,25p'
*   Trying 203.0.113.10:443...
* Connected to example.com (203.0.113.10) port 443 (#0)
> GET / HTTP/2
> host: example.com
> user-agent: curl/7.81.0
> accept: */*
< HTTP/2 301
< location: http://example.com/
< server: Apache
* Issue another request to this URL: 'http://example.com/'
> GET / HTTP/1.1
> Host: example.com
< HTTP/1.1 301 Moved Permanently
< Location: https://example.com/

Що це означає: У вас цикл зміни протоколу: HTTPS редиректить на HTTP, потім HTTP редиректить назад на HTTPS.
Зазвичай це дві різні джерела редиректів, що сперечаються (перезапис Apache плюс канонічність на рівні додатку).

Рішення: Знайдіть і видаліть одну зі сторін. Перевага: Apache забезпечує HTTPS; WordPress має бути налаштований так, щоб вважати, що він за HTTPS (через заголовки проксі).

Завдання 2: Перевірте, який MPM активний

cr0x@server:~$ apachectl -V | egrep -i 'server mpm|httpd_root|server_config_file'
Server MPM:     event
 -D HTTPD_ROOT="/etc/apache2"
 -D SERVER_CONFIG_FILE="apache2.conf"

Що це означає: У вас event MPM. Це добре для конкурентності, але ви не повинні використовувати mod_php.

Рішення: Переконайтеся, що PHP працює через PHP‑FPM (proxy_fcgi). Якщо бачите prefork — перевірте запас пам’яті і кількість воркерів.

Завдання 3: Перелік завантажених модулів (шукайте підозрюваних)

cr0x@server:~$ apachectl -M | egrep -i 'rewrite|security2|http2|deflate|brotli|headers|proxy_fcgi|mpm'
 headers_module (shared)
 http2_module (shared)
 proxy_fcgi_module (shared)
 rewrite_module (shared)
 security2_module (shared)
 deflate_module (shared)
 mpm_event_module (shared)

Що це означає: rewrite і security2 активні, плюс HTTP/2 і стиснення. Будь‑який із них може пояснити регресію.

Рішення: Якщо є 403: зосередьтеся на security2 і authz. Якщо є редиректи: дивіться rewrite і proxy‑заголовки. Якщо зависання клієнтів: інспектуйте http2/TLS.

Завдання 4: Підтвердіть, чи враховується .htaccess

cr0x@server:~$ apachectl -S 2>/dev/null | sed -n '1,40p'
VirtualHost configuration:
*:80                   example.com (/etc/apache2/sites-enabled/example.conf:1)
*:443                  example.com (/etc/apache2/sites-enabled/example-ssl.conf:1)
ServerRoot: "/etc/apache2"
Main DocumentRoot: "/var/www/html"

Це не показує AllowOverride прямо, тож перевірте vhost‑файл.

cr0x@server:~$ sudo apachectl -t -D DUMP_INCLUDES | sed -n '1,120p'
Included configuration files:
  (/etc/apache2/apache2.conf:215)
  (/etc/apache2/sites-enabled/example.conf:1)
  (/etc/apache2/sites-enabled/example-ssl.conf:1)

Тепер перегляньте директоровий блок:

cr0x@server:~$ sudo sed -n '1,140p' /etc/apache2/sites-enabled/example.conf
<VirtualHost *:80>
  ServerName example.com
  DocumentRoot /var/www/example.com/public

  <Directory /var/www/example.com/public>
    Options FollowSymLinks
    AllowOverride None
    Require all granted
  </Directory>
</VirtualHost>

Що це означає: .htaccess ігнорується. Правила перезапису WordPress у .htaccess не застосовуватимуться. Permalinks будуть 404 або поводитимуться дивно.

Рішення: Або встановіть AllowOverride FileInfo (або All, якщо вам подобається аудиторія), або перемістіть правила перезапису WordPress у vhost.

Завдання 5: Підтвердьте, що правила WordPress існують там, де ви їх очікуєте

cr0x@server:~$ sudo grep -n "BEGIN WordPress" -n /var/www/example.com/public/.htaccess
2:# BEGIN WordPress

Що це означає: Правила існують у .htaccess, але Завдання 4 показало, що Apache їх ігнорує.

Рішення: Не «перезбережіть permalinks» у wp‑admin і не імітуйте, що це вирішило проблему. Спочатку виправте Apache.

Завдання 6: Перевірте наявність MultiViews (воно ламає «гарні URL» дивними способами)

cr0x@server:~$ sudo apachectl -t -D DUMP_RUN_CFG | egrep -i 'multiviews|options'

Що це означає: Цей дамп не завжди вичерпний щодо Options на рівні директорії.

Рішення: Зробіть grep по конфігурації на MultiViews і вимкніть його для docroot WordPress.

cr0x@server:~$ sudo grep -RIn "MultiViews" /etc/apache2 | head
/etc/apache2/conf-enabled/negotiation.conf:12:Options +MultiViews

Рішення: Змініть на Options -MultiViews у vhost або секції Directory для WordPress.

Завдання 7: Підтвердіть підключення PHP‑хендлера (сокет FPM і права)

cr0x@server:~$ sudo apachectl -t -D DUMP_MODULES | grep -E 'proxy_fcgi|php'
 proxy_fcgi_module (shared)
cr0x@server:~$ sudo grep -RIn "SetHandler.*proxy:unix" /etc/apache2/sites-enabled/example-ssl.conf
38:  <FilesMatch \.php$>
39:    SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost/"
40:  </FilesMatch>
cr0x@server:~$ ls -l /run/php/php8.2-fpm.sock
srw-rw---- 1 www-data www-data 0 Dec 27 09:05 /run/php/php8.2-fpm.sock

Що це означає: Сокет існує й належить www-data. Apache має працювати як www-data (або бути в тій групі), щоб підключитися.

Рішення: Якщо бачите «permission denied» в логах Apache — виправляйте власність/групу сокета або користувача Apache, а не WordPress.

Завдання 8: Читайте логи помилок Apache навколо часу відмови (не пробіжно)

cr0x@server:~$ sudo tail -n 60 /var/log/apache2/error.log
[Wed Dec 27 09:11:02.331221 2025] [security2:error] [pid 22190] [client 198.51.100.24:51422] [id "949110"] [msg "Inbound Anomaly Score Exceeded"] [uri "/wp-admin/admin-ajax.php"]
[Wed Dec 27 09:11:02.331339 2025] [security2:error] [pid 22190] [client 198.51.100.24:51422] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5'"...

Що це означає: mod_security заблокував AJAX‑endpoint WordPress. id правила видно (тут 949110).

Рішення: Створіть цілеспрямоване виключення (видаліть або змініть те правило для цього vhost/шляху) і переконайтеся, що ви не приховуєте реальну атаку. Використовуйте audit‑логи для валідації шаблонів.

Завдання 9: Перегляньте модульний (ModSecurity) audit‑лог для повного контексту

cr0x@server:~$ sudo grep -n "949110" /var/log/apache2/modsec_audit.log | tail -n 5
245817:Message: Warning. Pattern match "..." at ARGS:action. [file "/usr/share/modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "78"] [id "949110"]

Що це означає: У вас є файл правил CRS і номер рядка. Це керована дія.

Рішення: Віддавайте перевагу виключенням за id правила + вузьким локаціям (наприклад, тільки wp-admin/admin-ajax.php), а не глобальному відключенню CRS.

Завдання 10: Доведіть поведінку перезапису за допомогою трасування RewriteLog‑подібного (метод для Apache 2.4)

Apache 2.4 не використовує старий директив RewriteLog. Увімкніть трасування модуля:

cr0x@server:~$ sudo apachectl -t -D DUMP_VHOSTS | sed -n '1,40p'
VirtualHost configuration:
*:80                   example.com (/etc/apache2/sites-enabled/example.conf:1)
*:443                  example.com (/etc/apache2/sites-enabled/example-ssl.conf:1)

Тимчасово додайте (для одного vhost) щось на кшталт:
LogLevel warn rewrite:trace3
потім перезавантажте й відтворіть. Після цього перегляньте логи:

cr0x@server:~$ sudo tail -n 40 /var/log/apache2/error.log
[rewrite:trace3] [pid 22501] mod_rewrite.c(477): [client 198.51.100.24:52011]  applying pattern '^/(.*)$' to uri '/'
[rewrite:trace3] [pid 22501] mod_rewrite.c(477): [client 198.51.100.24:52011]  rewrite '/' -> '/index.php'

Що це означає: Ви бачите, які патерни спрацювали і як URI було переписано.

Рішення: Якщо правило несподівано робить редирект — видаліть або звузьте його. Якщо правила WordPress ніколи не запускаються — контекст директорії/AllowOverride неправильний.

Завдання 11: Визначте «неправильний протокол за проксі», перевіривши заголовки запиту й середовище Apache

cr0x@server:~$ curl -s -D - -o /dev/null https://example.com/wp-login.php | egrep -i 'location:|server:|set-cookie:'
server: Apache
location: http://example.com/wp-login.php?redirect_to=...

Що це означає: WordPress генерує HTTP‑редиректи, тоді як браузер використовує HTTPS. Зазвичай відсутнє оброблення X-Forwarded-Proto або невірні налаштування site URL WordPress.

Рішення: Виправте передачу заголовків проксі і повідомте Apache/WordPress про оригінальний протокол. Також перевірте налаштування WordPress siteurl/home.

Завдання 12: Перевірте KeepAlive і таймаути (для повільних або «випадково відмовних» сайтів)

cr0x@server:~$ sudo apachectl -t -D DUMP_RUN_CFG | egrep -i 'KeepAlive|Timeout|MaxKeepAliveRequests|KeepAliveTimeout'
KeepAlive: On
MaxKeepAliveRequests: 100
KeepAliveTimeout: 5
Timeout: 60

Що це означає: Розумні дефолти. Якщо KeepAliveTimeout дуже великий — ви можете виснажити воркери під навантаженням.

Рішення: Для зайнятих WordPress‑сайтів тримайте KeepAliveTimeout малим (зазвичай 2–5 секунд) і довіряйте HTTP/2 для мультиплексування.

Завдання 13: Перевірте насичення воркерів через server-status Apache (якщо ввімкнено)

cr0x@server:~$ curl -s http://127.0.0.1/server-status?auto | egrep 'BusyWorkers|IdleWorkers|ReqPerSec|CPULoad'
CPULoad: .322
ReqPerSec: 18.2
BusyWorkers: 245
IdleWorkers: 0

Що це означає: У вас немає вільних воркерів. Нові з’єднання стоятимуть у черзі або відмовлятимуть. Це питання ємності Apache, а не «повільний» WordPress.

Рішення: Збільшуйте MaxRequestWorkers лише якщо є запас пам’яті. Інакше зменшіть KeepAliveTimeout, виправте повільні бекенд‑виклики і масштабуйтесь вшир.

Завдання 14: Перевірте насичення PHP‑FPM (досягнуто max children)

cr0x@server:~$ sudo tail -n 40 /var/log/php8.2-fpm.log
[27-Dec-2025 09:22:14] WARNING: [pool www] server reached pm.max_children setting (40), consider raising it
[27-Dec-2025 09:22:20] NOTICE: [pool www] child 17732 started

Що це означає: FPM насичений. Apache може бути в нормі; PHP‑воркери — ні.

Рішення: Не підвищуйте pm.max_children навмання. Виміряйте пам’ять на процес PHP, перевірте затримки БД і налаштовуйте згідно RAM і навантаження.

Завдання 15: Підтвердьте, що статичні ресурси кешуються коректно (а не HTML)

cr0x@server:~$ curl -sI https://example.com/wp-content/themes/site/style.css | egrep -i 'cache-control|expires|etag|vary|content-encoding'
cache-control: public, max-age=31536000
etag: "2c1b-5f3c1a4b"
vary: Accept-Encoding
content-encoding: br

Що це означає: Чудово для версіонованих ресурсів. Якщо тема використовує невідверсіоновані імена файлів — це може заморожувати старий CSS для користувачів.

Рішення: Використовуйте версіоновані URL (query string або хеш‑імена файлів), якщо ставите довгий max‑age. Не застосовуйте таке кешування до HTML‑відповідей.

Завдання 16: Швидко виявити помилку «кешувати все» для HTML

cr0x@server:~$ curl -sI https://example.com/ | egrep -i 'cache-control|set-cookie|vary'
cache-control: public, max-age=31536000
set-cookie: wordpress_logged_in=...

Що це означає: Ви публічно кешуєте HTML і при цьому ставите auth‑cookie. Ось як один користувач опиняється в сесії іншого.

Рішення: Виправте політику кешування: HTML зазвичай має бути private або no-store для шляхів з логіном, і ретельно контролюватися для анонімного трафіку.

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

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

Середня компанія мігрувала маркетинговий WordPress‑сайт за балансувальник, який термінував TLS.
Origin Apache спілкувався внутрішньо через HTTP — це нормально, якщо ви покажете додатку, як виглядає реальність.
Хтось сказав: «WordPress сам розбереться». Це речення варто заборонити в продакшн‑чаті.

За кілька хвилин після переключення мобільні користувачі потрапили в цикл редиректів. Десктопні інколи проходили.
HTTP‑трейс показав закономірність: запити приходили на балансувальник по HTTPS, форвардились до Apache як HTTP,
і WordPress генерував редиректи назад на HTTP, бо вважав себе запущеним на plain HTTP.

Команда спочатку ганялася за правилами перезапису. Потім — за HSTS. Потім — за CDN.
Прорив стався після банального: захоплення повного ланцюжка запит/відповідь від origin із заголовками і перевірки, які заголовки реально ставить балансувальник.

Origin не завжди отримував X-Forwarded-Proto: https через те, що один слушач не мав правила інжекції заголовка.
WordPress бачив суперечливі сигнали і намагався «виправити» URL у обох напрямках. Цикл не був містичним — він був детермінований.

Виправлення: зробити заголовки проксі консистентними, налаштувати Apache довіряти проксі та налаштувати WordPress враховувати forwarded proto як авторитетний.
Потім спростити Apache‑редиректи: один канонічний редирект — і все. Аутідж закінчився. Постмортем додав правило: жодної інфраструктурної міграції без curl‑транскрипту в тікеті.

Міні‑історія 2: Оптимізація, що зіграла навпаки

Інша організація вирішила «покращити продуктивність», відключивши сканування .htaccess.
Ідея була правильна: AllowOverride None зменшує файлові звернення і може позбутися класу конфігураційних дрейфів.
Проблема була в виконанні: вони ввімкнули перемикач у п’ятницю вдень, не перенісши правила перезапису у vhost.

Сайт не впав повністю. Він зробив щось гірше: він майже працював, крім усього, що дозволяло називати його веб‑сайтом.
Головна сторінка завантажувалася, але пости давали 404. Категорійні сторінки 404. JSON‑ендпоінти, що використовує блочний редактор, відмовляли.
Підтримка описувала це як «рандом». Нічого не випадково; це було розпадання маршрутизації за шляхом.

Вони відкотили і назвали це «проблемою Apache». Не зовсім. Це було питанням управління змінами.
Відключення AllowOverride — це міграція. Поводьтеся з нею як з міграцією: перенесіть правила, протестуйте permalinks, wp‑admin, wp‑json,
і тільки потім видаляйте підтримку .htaccess.

Довгостроковий результат був позитивний: згодом вони перенесли правила у vhost, додали інтеграційні тести, які curl‑ять набір репрезентативних URL,
і прибрали багато застарілих фрагментів rewrite з .htaccess. Продуктивність покращилася, конфіг став простішим.
Але урок простий: оптимізації не варті, якщо вони видаляють функціональність.

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

Велике підприємство мало флот WordPress із загальною Apache‑базою: стандартні модулі, стандартні заголовки, стандартна TLS‑політика.
Кожен сайт також мав свої плагіни й редакційний персонал, іншими словами — «непередбачувані payload‑и запитів».
Вони запускали mod_security з CRS, бо так вимагала комплаєнс‑політика.

Щоразу, коли оновлення плагіна тригерило false positive, інші команди хотіли «тимчасово вимкнути WAF».
Платформна команда відмовлялася. Натомість у них була процедура: збирати мод_security audit‑івент, визначити id правила,
підтвердити, що payload легітимний, написати вузьке виключення для сайту й endpoint‑у, і прикріпити це до change request.
Це було нудно. Це також було відтворювано.

Одного ранку понеділка хвиля запитів почала бити по /wp-login.php з дивними параметрами.
mod_security заблокував більшість. Декілька легітимних дій адмінів теж блокувалися, і редактори швидко поскаржилися.
Команда скористалася встановленим процесом: налаштували одне правило для конкретного admin‑ajax виклику, зберігши загальний захист.

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

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

Цикл редиректів (ERR_TOO_MANY_REDIRECTS)

Симптом: Браузер застрягає між HTTP і HTTPS або між www і non‑www.

Корінна причина: Конфліктна канонізація між Apache rewrite, налаштуваннями WordPress і поведінкою проксі/CDN.

Виправлення: Виберіть один рівень, що забезпечує канонічні хост/схему (зазвичай edge або Apache). Переконайтеся, що X-Forwarded-Proto і X-Forwarded-Host консистентні, а в WordPress siteurl/home відповідають публічному URL.

Permalinks 404, але головна сторінка працює

Симптом: / завантажується, /2025/… дає 404, /wp-json не працює.

Корінна причина: .htaccess ігнорується через AllowOverride None, або правила перезапису відсутні у vhost.

Виправлення: Ввімкніть AllowOverride FileInfo для docroot або перенесіть правила перезапису WordPress у vhost.

403 Forbidden на wp-admin або admin-ajax

Симптом: Адмін‑сторінки частково завантажуються; AJAX‑виклики падають; редактор показує помилки; в логах 403.

Корінна причина: Помилкове спрацювання mod_security або неправильна authz‑конфігурація Apache (Require, заблоковані методи тощо).

Виправлення: Перевірте лог помилок Apache на id правил; чітко налаштуйте виключення mod_security. Якщо причина authz — виправте права директорій і додайте Require all granted там, де потрібно.

502 Bad Gateway після «дрібного» оновлення PHP

Симптом: Apache повертає 502 для всіх PHP‑сторінок; статичні файли віддаються.

Корінна причина: proxy_fcgi все ще вказує на старий шлях сокета PHP‑FPM, або сервіс FPM не запущений.

Виправлення: Перевірте шлях у SetHandler і стан сервісу; перезавантажте Apache після оновлення. Підтвердіть права сокета.

Повільний сайт, CPU в нормі, але користувачі скаржаться

Симптом: Велика затримка, іноді таймаути, без очевидного підвищення CPU.

Корінна причина: Голод воркерів через великий KeepAliveTimeout, насичення бекенду (FPM max children) або затримки диска для сесій/завантажень.

Виправлення: Перевірте BusyWorkers/IdleWorkers, логи FPM щодо max_children і зменшіть KeepAliveTimeout. Потім профілюйте повільні ендпоінти.

Зміни теми/JS не показуються користувачам

Симптом: «Я очистив кеш» стає кардіо‑рутиною служби підтримки.

Корінна причина: Агресивний Cache‑Control/Expires для невідверсіонованих ресурсів.

Виправлення: Версіонуйте ресурси (хеш‑імена або query string). Тримайте довгий кеш лише для версіонованих файлів; коротший для динамічних чи часто оновлюваних.

REST API ламається, блочний редактор не працює, але фронтенд виглядає ок

Симптом: Редактор не зберігає, показує «Publishing failed», або embeds відмовляють.

Корінна причина: mod_security блокує JSON‑payloadи, правила перезапису не маршрутять /wp-json, або заголовки/CORS неправильні.

Виправлення: Переконайтеся, що /wp-json/ повертає 200 з origin, налаштуйте mod_security і забезпечте, щоб правила перезапису/front controller застосовувалися до цього шляху.

Жарт №2: Найнебезпечніша фраза в опсах — «це просто зміна заголовка».

Контрольні списки / покроковий план

Контрольний список A: Стабілізація зламаного WordPress на Apache (30–60 хвилин)

  1. Захопіть один фейл‑запит за допомогою curl (-svL) і додайте вивід до інцидентного тікета.
  2. Зкорелюйте часову мітку з логом помилок Apache і логом PHP‑FPM.
  3. Класифікуйте як редирект/403/5xx/повільний і оберіть набір модулів для перевірки.
  4. Підтвердіть, чи враховується .htaccess (AllowOverride) і чи існують правила перезапису там, де очікуєте.
  5. Перевірте спрацьовування mod_security за id правила; не здогадуйтеся.
  6. Підтвердіть шлях сокета PHP‑FPM і права; переконайтеся, що сервіс здоровий.
  7. Виміряйте насичення воркерів (BusyWorkers/IdleWorkers; попередження FPM про max_children).
  8. Зробіть одну зміну, що усуває первинний режим відмови (наприклад, видалити конфліктний редирект, додати вузьке WAF‑виключення, виправити шлях сокета).
  9. План відкату: переконайтеся, що можна швидко повернути конфіг (попередній vhost, відома добра версія стану модулів).
  10. Перевірте знову: головна сторінка, один permalink, wp‑admin login, admin‑ajax, wp‑json.

Контрольний список B: Запобігання наступному інциденту (те, що ніхто не планує)

  1. Стандартизуйте канонізацію: вирішіть, де живуть редиректи (edge vs Apache vs app). Документуйте це.
  2. Припиніть дублювання правил між Apache і WordPress. Має бути один канонічний ланцюг редиректів, не три.
  3. Перенесіть rewrite‑конфіг у vhost, якщо можете, і тримайте AllowOverride None для продуктивності й детермінізму.
  4. Логуйте те, що потрібно: час запиту, upstream‑час, статус, user agent, хост, X-Forwarded-Proto.
  5. Тримайте mod_security, але керуйте ним: збереження audit‑логів, виключення за id правил і простий процес затвердження.
  6. Тестіруйте навантаження правдоподібно: включайте wp‑admin та wp‑json виклики, не тільки анонімні хіти головної сторінки.
  7. Обмежуйте радіус ураження: per‑vhost конфіги, поетапні релізи та feature‑флаги для ризикових модулів (HTTP/2, brotli) де можливо.

Контрольний список C: Безпечний процес змін Apache для WordPress

  1. Робіть diff конфігів у PR (або хоча б change request) і вимагайте іншого рев’ю.
  2. Запускайте apachectl -t перед reload.
  3. Деплойте на один canary vhost/хост і запускайте скриптований набір curl‑тестів (головна, permalinks, wp‑login, wp‑admin, wp‑json).
  4. Спостерігайте логи помилок і рівні 4xx/5xx 15–30 хвилин.
  5. Розгортаючись поступово. Якщо ваші інструменти не підтримують поступові релізи — інструменти самі по собі ризик.

Питання й відповіді

1) Чи варто використовувати .htaccess для WordPress на Apache?

Якщо ви на шаред‑хостингу, ймовірно, доведеться. Якщо ви управляєте сервером — віддавайте перевагу vhost‑конфігу.
.htaccess коштує продуктивності і створює «невидиму конфігурацію», яку ваш деплой‑процес важко версіонує.

2) Який найшвидший спосіб сказати, чи проблема в rewrite‑правилах?

Curl‑ніть відомий permalink і дивіться код статусу. Потім перевірте, чи Apache враховує .htaccess (AllowOverride).
Якщо permalinks 404 і AllowOverride встановлено в None — ви знайшли причину: Apache ігнорує правила.

3) Чому wp‑admin працює, а блочний редактор ламається?

Блочний редактор сильно покладається на REST API‑endpoint‑и під /wp-json та AJAX‑виклики.
mod_security, зламані rewrites або заблоковані методи можуть вибірково ламати ці endpoint‑и, лишаючи HTML‑сторінки цілими.

4) Чи варто використовувати mod_security для WordPress?

Так, якщо ви експлуатуєте його професійно: audit‑логи, вузькі виключення і періодичний перегляд.
Ні, якщо ваша операційна модель — «вимикаємо, коли він лає».

5) prefork vs event MPM: що вибрати для WordPress?

Якщо ви використовуєте PHP‑FPM, у більшості випадків обирайте event MPM. Якщо ви використовуєте mod_php (не рекомендовано для сучасних сетапів), будете прив’язані до prefork.
Правильна відповідь зазвичай «event + PHP‑FPM», далі налаштовуйте воркери за пам’яттю й трафіком.

6) Чи може HTTP/2 зламати мій WordPress‑сайт?

Зазвичай ні, але воно може вивести на поверхню неправильні налаштування: TLS‑квірки, багаті проксі або некоректну поведінку кешування.
Якщо увімкнення HTTP/2 корелює з зависаннями клієнтів або частковими завантаженнями — тимчасово вимкніть HTTP/2 на одному vhost і порівняйте.

7) Чому я отримую 403 тільки на admin-ajax.php?

admin-ajax часто несе payloadи, що виглядають як атака для загальних WAF‑правил.
Подивіться лог помилок Apache; часто там буде id правила mod_security. Виключайте вузько для цього endpoint‑у й id правила.

8) Мій CSS/JS кешується вічно після ввімкнення mod_expires. Як виправити без відключення кешування?

Тримайте довгі терміни кешування тільки для версіонованих ресурсів. Якщо імена файлів не версіоновані, додайте версії (хеші або query string) у збірці/деплої вашої теми.
Скоротіть кеш для невідверсіонованих файлів. Кешування не ворог — невідверсіоновані ресурси — так.

9) WordPress постійно редиректить на HTTP, хоча сайт HTTPS. Це Apache чи WordPress?

Майже завжди це «невідповідність реальності проксі». Apache бачить HTTP від балансувальника і передає цю інформацію PHP.
Виправте forwarded proto/host і налаштування site URL у WordPress, потім видаліть дублюючі редиректи.

10) Який мінімально безпечний набір модулів Apache для WordPress?

Зазвичай: rewrite, headers, TLS‑модулі, PHP‑хендлер (proxy_fcgi), і опційно deflate або brotli.
Додавайте http2, якщо середовище його коректно підтримує. Додавайте mod_security, якщо можете ним правильно управляти.

Висновок: що робити далі (і що припинити робити)

WordPress часто звинувачують у багатьох проблемах Apache, бо це те, що бачать користувачі.
Насправді повторювані винуватці — це конфігураційні шари: правила перезапису, що сперечаються, .htaccess, що ігнорується,
WAF‑правила, що блокують легітимний трафік, і «поліпшення продуктивності», що не були протестовані на реальних endpoint‑ах.

Наступні кроки, що швидко дають користь:

  • Запишіть одну політику канонізації (хост + схема) і застосовуйте її в одному місці.
  • Визначте, чи ви «.htaccess‑шоп», чи «vhost‑шоп». Виберіть одне. Виконуйте послідовно.
  • Увімкніть логи, які потрібні для швидкої діагностики (включно з часом запиту), і зробіть «curl‑транскрипт» адвентиною практикою інцидентів.
  • Розглядайте виключення mod_security як код: вузькі, переглянуті й обґрунтовані.
  • Коли налаштовуєте Apache — робіть це з вимірюваннями ємності (BusyWorkers/FPM насичення), а не за відчуттями.

Припиніть робити це:

  • Припиніть накладати редиректи на трьох рівнях і дивуватися, чому вийшла Мебіус‑стрічка.
  • Припиніть глобально вимикати засоби безпеки через одне спрацювання плагіна.
  • Припиніть відправляти заголовки кешування для HTML так само, як для зображень.

Мета — не хитрий конфіг Apache. Мета — WordPress, що залишається доступним, швидким і виходить із збоїв такими способами, які можна діагностувати до обіду.

← Попередня
MySQL vs PostgreSQL: обмеження пам’яті в Docker — як зупинити тихе зниження продуктивності
Наступна →
Реальне тестування CPU: простий метод для вашого навантаження

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