WordPress — 502 Bad Gateway: PHP‑FPM, Nginx, Cloudflare — як знайти винуватця

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

502 — це найгірший вид простою: такий, що спричиняє взаємні звинувачення. Cloudflare вказує на ваш origin. Nginx каже на PHP‑FPM. PHP‑FPM каже на WordPress. WordPress вказує на плагін, написаний у 2013 році кимось, хто тепер знайшов спокій у теслярстві.

Цей посібник — прагматичний підхід для продакшену, щоб припинити гадання. Ви визначите, де запит загинув, чому загинув і яка зміна фактично це виправить — без перетворення сервера на виставку наукових проєктів.

Що насправді означає 502 (у цьому стеку)

502 Bad Gateway — це не помилка WordPress. Це помилка проксі. Щось, що виступає шлюзом (Cloudflare, Nginx, балансувальник), намагалося поговорити з upstream (ваш origin, PHP‑FPM) і отримало хибні дані, нічого не отримало або відбулося очікування з таймаутом, що породжує 502.

Ця різниця важлива, бо налагоджувати 502 треба шляхом доведення, де відбулася відмова. Ваше завдання — не «поправити WordPress». Ваше завдання — «виявити перший компонент, який не виконав свою роботу». Коли ви це знаєте, виправлення стає очевидним. Часто нудним. Зазвичай ефективним.

Дві поширені реальності:

  • 502 від Cloudflare: Cloudflare не отримав коректної відповіді від вашого origin вчасно або origin закрив з’єднання. Cloudflare не виконував ваш PHP‑код. Він звернувся до origin і його проігнорували.
  • 502 від Nginx: Nginx не зміг зв’язатися з PHP‑FPM (відмова сокета, відмова в доступі, upstream закрив з’єднання, upstream вичерпав час очікування). Nginx теж не виконує PHP‑код — він намагається його передати, і передача зазнає невдачі.

Ось ментальна модель, яку я використовую на виклику: 502 — це помилки передачі. Найцінніше питання: яка саме передача?

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

Якщо нічого не запам’ятаєте — запам’ятайте цей порядок. Він розсіює звинувачення і швидко виводить на винуватця.

Крок 1: Класифікуйте джерело 502 (edge чи origin)

  • Якщо користувачі бачать сторінку з брендом Cloudflare, починайте з заголовків Cloudflare і перевірки досяжності origin.
  • Якщо користувачі бачать вашу сторінку з помилкою або plain «502 Bad Gateway» від вашого сервера, починайте з логів Nginx і PHP‑FPM.

Крок 2: Перевірте лог помилок Nginx на рядок upstream

Це найшвидша сироватка правди. Точна фраза підкаже, чи це «upstream timed out», «connect() failed», «upstream prematurely closed connection» або «no live upstreams». Кожна відповідає іншому виправленню.

Крок 3: Перевірте здоров’я та ємність PHP‑FPM

Шукайте max_children reached, записи slowlog або незапущений/недоступний pool‑сокет. Не налаштовуйте наосліп. Підтвердіть насичення і запас пам’яті.

Крок 4: Корелюйте за часом і шляхом запиту

Більшість 502 не випадкові. Вони кластеризуються за певними endpoint‑ами (wp‑admin, wp‑cron.php, /checkout, AJAX‑дія). Знайдіть шлях. Потім знайдіть, який код він виконує.

Крок 5: Визначте: підключення, ємність чи латентність

  • Підключення: права сокета, listen backlog, SELinux/AppArmor, фаєрвол, неправильна адреса upstream.
  • Ємність: замало воркерів, завантажений CPU, завислий MySQL, очікування IO, тиск пам’яті.
  • Латентність: повільні запити, зовнішні API, проблемний плагін, повільний диск, зависання DNS.

Жарт #1: 502 — це спосіб вашого сервера сказати «Я намагався», що також те, що я пишу в ретроспективах інцидентів, коли графіків немає.

Слід за запитом: Cloudflare → Nginx → PHP‑FPM → WordPress

Уявіть запит як естафету:

  1. Браузер → Cloudflare: користувач звертається до edge. Cloudflare застосовує правила WAF, кешування, перевірки ботів.
  2. Cloudflare → Origin (ваш сервер): Cloudflare підключається до вашого Nginx/Apache, зазвичай по 443.
  3. Nginx → PHP‑FPM: Nginx проксує PHP‑запити до UNIX‑сокета або TCP‑порту.
  4. PHP‑FPM → WordPress: PHP виконує код, викликає MySQL, можливо Redis, можливо зовнішні API.
  5. Відповідь повертається назад.

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

Цікаві факти та контекст (щоб помилки набули сенсу)

  • Факт 1: «Bad Gateway» — це HTTP‑статус, визначений для проміжних компонентів, а не для прикладного коду. Ваш PHP‑додаток зазвичай не генерує 502 навмисно.
  • Факт 2: Nginx став популярним частково через event‑driven модель, що ефективно обробляє багато неактивних з’єднань — добре для повільних клієнтів, але не чарівна паличка для повільних upstream.
  • Факт 3: PHP‑FPM — де‑факто менеджер процесів для PHP, бо ізолює виконання PHP і дає веб‑серверу залишатися легким; саме через це й виникають «помилки передачі».
  • Факт 4: «522» від Cloudflare відомий, але «502/504 на edge» часто — просто виражена інакше повільність origin: таймаути — це політика, а не універсальна істина.
  • Факт 5: admin‑ajax у WordPress може стати точкою високої конкуренції; це по суті RPC‑endpoint, яким часто зловживають плагіни.
  • Факт 6: Налаштування keepalive і buffering історично підбиралися під дорогі upstream і повільних клієнтів; сучасні стекі успадкували ці параметри, і неправильно налаштовані вони можуть підсилити відмови.
  • Факт 7: Класичне попередження «max_children reached» у PHP‑FPM само по собі не є помилкою; це сигнал ємності, що часто корелює з 502/504 у проксі.
  • Факт 8: UNIX‑сокети трохи швидші за TCP loopback для Nginx→FPM, але помилки з правами та шляхами трапляються з сокетами частіше, ніж з TCP.
  • Факт 9: Багато «випадкових» 502 трапляються під час деплоїв, бо reload PHP‑FPM може коротко зникати сокет або скидати з’єднання; важлива коректна конфігурація graceful‑reload.

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

Це ті завдання, які я справді виконую під тиском. Кожне має три частини: команда, що означає вивід, і рішення. Підлаштуйте імена сервісів (php8.2-fpm vs php-fpm) під вашу дистрибуцію.

Завдання 1: Підтвердіть, де генерується 502 (заголовки)

cr0x@server:~$ curl -sS -D - -o /dev/null https://example.com/ | sed -n '1,20p'
HTTP/2 502
date: Fri, 26 Dec 2025 14:03:12 GMT
content-type: text/html; charset=UTF-8
server: cloudflare
cf-ray: 88b12345abcd1234-FRA

Що це означає: server: cloudflare і cf-ray вказують, що 502 повертає edge. Це не виправдовує origin.

Рішення: Негайно протестуйте origin напряму (Завдання 2). Якщо origin ок — перевіряйте налаштування Cloudflare/WAF. Якщо origin теж падає — налагоджуйте origin.

Завдання 2: Обійти Cloudflare, щоб протестувати origin

cr0x@server:~$ curl -sS -D - -o /dev/null --resolve example.com:443:203.0.113.10 https://example.com/ | sed -n '1,20p'
HTTP/2 200
server: nginx
content-type: text/html; charset=UTF-8

Що це означає: Це влучає в 203.0.113.10, використовуючи SNI для example.com. Ви бачите відповідь origin.

Рішення: Якщо origin повертає 200, а Cloudflare — 502, підозрюйте проблеми з підключенням Cloudflare→origin, невідповідність режиму TLS, бійку WAF/ботів або rate limiting проти конкретних IP Cloudflare. Якщо origin також падає — рухайтесь у бік Nginx/PHP‑FPM.

Завдання 3: Подивіться точний рядок помилки upstream у Nginx

cr0x@server:~$ sudo tail -n 50 /var/log/nginx/error.log
2025/12/26 14:03:10 [error] 21344#21344: *99172 connect() to unix:/run/php/php8.2-fpm.sock failed (111: Connection refused) while connecting to upstream, client: 172.67.10.20, server: example.com, request: "GET / HTTP/2.0", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock:", host: "example.com"

Що це означає: Nginx намагався підключитися до сокета PHP‑FPM і отримав Connection refused. Це не повільний плагін. Це зламана передача.

Рішення: Перевірте, чи PHP‑FPM запущено і слухає на очікуваному сокеті (Завдання 4, Завдання 5). Якщо запущено — можлива невідповідність шляху сокета, права або crash‑loop.

Завдання 4: Перевірте стан служби PHP‑FPM

cr0x@server:~$ systemctl status php8.2-fpm --no-pager
● php8.2-fpm.service - The PHP 8.2 FastCGI Process Manager
     Loaded: loaded (/lib/systemd/system/php8.2-fpm.service; enabled)
     Active: active (running) since Fri 2025-12-26 13:58:22 UTC; 4min 53s
       Docs: man:php-fpm8.2(8)
   Main PID: 20811 (php-fpm8.2)
     Status: "Processes active: 12, idle: 4, Requests: 983, slow: 3, Traffic: 0.8req/sec"

Що це означає: Служба працює. Це не гарантує, що вона слухає там, де Nginx очікує.

Рішення: Підтвердіть існування сокета та власника (Завдання 5) і конфігурацію pool для listen.

Завдання 5: Перевірте, чи існує сокет і чи коректні права

cr0x@server:~$ sudo ls -l /run/php/php8.2-fpm.sock
srw-rw---- 1 www-data www-data 0 Dec 26 13:58 /run/php/php8.2-fpm.sock

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

Рішення: Якщо Nginx працює під користувачем nginx, налаштуйте групи або встановіть listen.owner, listen.group та listen.mode у конфігу FPM pool. Якщо сокет відсутній — PHP‑FPM не створює його — дивіться логи (Завдання 6).

Завдання 6: Читайте логи PHP‑FPM і journal на предмет помилок pool

cr0x@server:~$ sudo journalctl -u php8.2-fpm -n 80 --no-pager
Dec 26 14:02:57 server php-fpm8.2[20811]: [WARNING] [pool www] server reached pm.max_children setting (20), consider raising it
Dec 26 14:03:01 server php-fpm8.2[20811]: [ERROR] [pool www] child 22109 exited on signal 11 (SIGSEGV) after 12.345678 seconds from start
Dec 26 14:03:01 server php-fpm8.2[20811]: [NOTICE] [pool www] child 22117 started

Що це означає: Дві окремі проблеми: насичення ємності (max_children) і нестабільність (segfault). Segfault часто викликає buggy‑extension, проблема з OPcache або корупція пам’яті.

Рішення: Якщо бачите попередження про max_children — підтвердіть кореляцію з 502 і налаштовуйте обережно (Завдання 11). Якщо бачите segfault — відключайте підозрілі розширення, перевіряйте пакети PHP і розгляньте відкат недавніх змін. Розглядайте segfault як інцидент надійності, а не «WordPress робить дивно».

Завдання 7: Переконайтесь, що Nginx вказує на правильний upstream

cr0x@server:~$ sudo nginx -T 2>/dev/null | sed -n '/fastcgi_pass/,+3p'
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

Що це означає: Nginx очікує цей шлях сокета. Якщо pool FPM слухає на іншому шляху або TCP‑порті — у вас «розподелена система» в одній машині.

Рішення: Вирівняйте fastcgi_pass з listen у pool. Уникайте «кількох істин» (сокет в одному файлі, TCP‑порт в іншому).

Завдання 8: Перевірте таймаути upstream vs помилки підключення

cr0x@server:~$ sudo grep -E "upstream timed out|prematurely closed|Connection refused|no live upstreams" -n /var/log/nginx/error.log | tail -n 8
41288:2025/12/26 14:01:21 [error] 21344#21344: *98811 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 162.158.90.12, server: example.com, request: "GET /wp-admin/ HTTP/2.0", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock:", host: "example.com"
41307:2025/12/26 14:03:10 [error] 21344#21344: *99172 connect() to unix:/run/php/php8.2-fpm.sock failed (111: Connection refused) while connecting to upstream, client: 172.67.10.20, server: example.com, request: "GET / HTTP/2.0", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock:", host: "example.com"

Що це означає: У вас є і таймаути, і відмови підключення. Це свідчить про періодичну недоступність FPM (reload/crash) та повільність upstream, коли він доступний.

Рішення: Спершу виправте стабільність (щоб уникнути відмов), потім адресуйте повільність. Система, що постійно повільна, простіша для налаштування, ніж така, що випадково зникає.

Завдання 9: Перегляньте активні з’єднання і тиск на listen backlog

cr0x@server:~$ sudo ss -xlp | grep php8.2-fpm.sock
u_str LISTEN 0 4096 /run/php/php8.2-fpm.sock 113217 * 0 users:(("php-fpm8.2",pid=20811,fd=8))

Що це означає: Сокет слухає, backlog 4096. Якщо backlog малий і ви маєте спайки трафіку — під час навантаження з’являться помилки підключення.

Рішення: Якщо backlog низький — налаштуйте listen.backlog у FPM pool і переконайтесь, що ліміти ядра не маленькі. Але не використовуйте backlog, щоб приховати недостатню кількість воркерів.

Завдання 10: Перевірте CPU, load, IO wait (сервер вимикається?)

cr0x@server:~$ uptime; mpstat -P ALL 1 3; vmstat 1 5
 14:03:33 up 36 days,  3:22,  2 users,  load average: 12.44, 10.81, 8.03
Linux 6.1.0 (server)  12/26/2025  _x86_64_  (4 CPU)

Average:     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
Average:     all   72.10    0.00   18.34    6.22    0.00    0.78    0.00    0.00    0.00    2.56
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
12  1      0  14200  12000 820000    0    0   120   980 4200 8800 74 19  2  5  0

Що це означає: CPU завантажений і є IO wait. Якщо PHP‑FPM потребує CPU, а машина завантажена — Nginx таймаути не забаряться.

Рішення: Зменшіть конкуренцію (rate limit, кешування), оптимізуйте «гарячі» шляхи або додайте ресурси. Перед тим як підвищувати налаштування FPM, підтвердіть запас по пам’яті (Завдання 11), щоб не «вирішити» 502 викликом OOM‑kill.

Завдання 11: Виміряйте пам’ять воркерів PHP‑FPM, щоб безпечно налаштувати pm.max_children

cr0x@server:~$ ps -ylC php-fpm8.2 --sort:rss | head -n 8
S   UID   PID  PPID  C PRI NI   RSS    SZ WCHAN  TTY          TIME CMD
S    33 22117 20811  0  80  0 126432 210944 -     ?        00:00:01 php-fpm: pool www
S    33 22098 20811  0  80  0 118944 205312 -     ?        00:00:02 php-fpm: pool www
S    33 22077 20811  0  80  0 112880 198656 -     ?        00:00:02 php-fpm: pool www
S    33 22031 20811  0  80  0 108220 190112 -     ?        00:00:03 php-fpm: pool www
S    33 21990 20811  0  80  0 104112 186880 -     ?        00:00:04 php-fpm: pool www

Що це означає: RSS приблизно ~110–125 MB на воркер у цьому знімку. Саме це число важливе при підвищенні pm.max_children.

Рішення: Якщо у вас 2 GB для PHP‑воркерів, ви не ставите max_children у 80 «через трафік». Ви підбираєте значення, яке RAM може оплатити, з запасом для MySQL, кешу ОС і піків.

Завдання 12: Увімкніть і читайте PHP‑FPM slowlog, щоб знайти справжніх винуватців

cr0x@server:~$ sudo grep -nE "request_slowlog_timeout|slowlog" /etc/php/8.2/fpm/pool.d/www.conf | tail -n 5
277:request_slowlog_timeout = 5s
278:slowlog = /var/log/php8.2-fpm/www-slow.log
cr0x@server:~$ sudo tail -n 40 /var/log/php8.2-fpm/www-slow.log
[26-Dec-2025 14:01:22]  [pool www] pid 22031
script_filename = /var/www/example.com/public/wp-admin/admin-ajax.php
[0x00007f4c8b8f2a30] curl_exec() /var/www/example.com/public/wp-includes/Requests/Transport/cURL.php:204
[0x00007f4c8b8f28a0] request() /var/www/example.com/public/wp-includes/class-wp-http.php:420
[0x00007f4c8b8f2500] post() /var/www/example.com/public/wp-includes/class-wp-http.php:634
[0x00007f4c8b8f1d40] call_external_api() /var/www/example.com/public/wp-content/plugins/some-plugin/api.php:88

Що це означає: Конкретний шлях (admin-ajax.php) застряг в очікуванні зовнішнього API через cURL.

Рішення: Не «збільшуйте таймаути». Ви ізолюєте або фіксуєте поведінку плагіна: додаєте кеш, circuit breaker, асинхронні задачі або відключаєте плагін. І встановлюєте розумний upstream таймаут, щоб уникнути накопичення воркерів.

Завдання 13: Перевірте затримки MySQL/MariaDB (поширений прихований upstream)

cr0x@server:~$ mysql -e "SHOW PROCESSLIST\G" | sed -n '1,40p'
*************************** 1. row ***************************
     Id: 18821
   User: wpuser
   Host: localhost
     db: wordpress
Command: Query
   Time: 42
  State: Sending data
   Info: SELECT option_name, option_value FROM wp_options WHERE autoload = 'yes'

Що це означає: Запит виконуються 42 секунди. Це може блокувати PHP‑воркери достатньо довго, щоб спричинити Nginx upstream таймаути і Cloudflare 5xx.

Рішення: Знайдіть, чому він повільний: розростання таблиць, відсутні індекси, конкурентний доступ до диска або плагін, що зберігає великі autoload опції. Виправте вузьке місце в БД або ви просто переставляєте місця в човні.

Завдання 14: Перевірте тиск на диск і здоров’я файлової системи (так, сховище важливе)

cr0x@server:~$ df -hT /var /var/www; iostat -xz 1 3
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4  120G  111G  3.6G  97% /
/dev/nvme0n1p2 ext4  120G  111G  3.6G  97% /
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          68.12    0.00   17.43    9.87    0.00    4.58

Device            r/s     w/s    rkB/s    wkB/s  await  svctm  %util
nvme0n1          45.0    60.0   3200.0   5400.0  18.2   0.7   92.0

Що це означає: Коренева файлова система зайнята на 97% і диск завантажений на 92% з помітним await. Майже повні диски викликають каскадні відмови: записи логів не проходять, тимчасові файли не створюються, БД гальмує, PHP блокується.

Рішення: Звільніть місце зараз (логи, старі резервні копії), потім виправте план зберігання. Не намагайтесь налаштувати PHP‑FPM на «крикучому» диску.

Завдання 15: Переконайтесь, що Cloudflare→origin підключення не блокується

cr0x@server:~$ sudo iptables -S | sed -n '1,60p'
-P INPUT DROP
-P FORWARD DROP
-P OUTPUT ACCEPT
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -s 198.51.100.0/24 -j ACCEPT

Що це означає: Дозволений доступ на 443 лише для конкретного /24. Якщо IP‑диапазони Cloudflare не оновлені в allowlist — помилки на edge будуть залежати від того, який PoP вас обслуговує.

Рішення: Відкрийте 443 для потрібних адрес або підтримуйте актуальний allowlist. «Ми дозволяємо лише Cloudflare IP» — це валідна стратегія, якщо ви її дійсно підтримуєте.

Режими відмов Nginx, що дають 502

1) «connect() failed (111: Connection refused)»

Це означає, що Nginx намагався підключитися до upstream (сокет FPM або TCP‑порт) і ОС відмовила. Поширені причини:

  • PHP‑FPM не працює або в crash‑loop.
  • Nginx вказує на неправильний шлях сокета або порт.
  • Під час reload сокет тимчасово зник, і Nginx зловив рестарт у невдалому моменті.

Виправлення: Забезпечте стабільність upstream. Перевірте шлях сокета у nginx -T, стежте за здоров’ям PHP‑FPM і уникайте агресивних циклів рестартів під час деплоїв.

2) «upstream timed out (110: Connection timed out) while reading response header from upstream»

Nginx підключився до PHP‑FPM, вислав запит і довго чекав заголовків. Це класичний випадок «PHP повільний або насичений». Можливі винуватці:

  • Всі воркери зайняті (pm.max_children reached), тому запити чекають у черзі.
  • Один або кілька endpoint‑ів повільні (admin‑ajax, checkout, cron).
  • Затримки БД або сховища блокують PHP, тож FPM не відповідає.
  • Зовнішні API‑виклики зависають і фіксують воркери.

Виправлення: Використайте slowlog, щоб ідентифікувати шлях коду. Потім виправте причину повільності. Підвищення таймаутів Nginx без вирішення накопичення воркерів перетворює 502 на 504 і робить графіки латентності схожими на модерн‑мистецтво.

3) «upstream prematurely closed connection»

Upstream прийняв з’єднання, потім впав або закрив його, не відправивши повну відповідь. Поширені причини:

  • Робочий PHP впав (segfault, fatal error, OOM kill).
  • Проблеми з FastCGI‑буферами при великих заголовках або відповідях (рідше для HTML WordPress, частіше з дивними плагінами).
  • Неправильні fastcgi‑налаштування.

Виправлення: Перевірте логи PHP‑FPM на крашi і системні логи на OOM‑kill. Розглядайте падіння як справжню багу. Якщо це заголовки — перевірте надто великі cookies або заголовки, що генерує плагін.

4) Відмова в доступі до сокета (Permission denied)

Nginx не може відкрити FPM‑сокет через права файлової системи або SELinux/AppArmor. В логах буде (13: Permission denied).

Виправлення: Забезпечте, щоб сокет був читабельний/записуваний робітником Nginx. Якщо SELinux у режимі enforcing — потрібний правильний контекст, а не молитви.

Режими відмов PHP‑FPM, що дають 502

1) pm.max_children reached (стеля ємності)

Це найпоширеніший сценарій «працювало доти, поки не перестало». Коли всі дочірні процеси зайняті — нові запити чекають. Якщо черга чекає довше за таймаут Nginx — ви отримуєте 502/504.

Що робити:

  • Заміряйте пам’ять і CPU воркерів перед підвищенням ліміту.
  • Спочатку виправте повільні запити. Більше воркерів може збільшити навантаження на БД і погіршити ситуацію.
  • Розгляньте окремі пулі для адмінки і публічних запитів, якщо вам важлива доступність.

2) Повільні запити, що фіксують воркери (тихий вбивця)

Один повільний endpoint шкодить не лише йому. Він забирає воркер. При конкуренції повільні запити стають проблемою чергування, потім — відмовою.

Виправлення: Використовуйте slowlog. Ідентифікуйте стек викликів. Виправте код, додайте кешування, зменшіть кількість викликів, встановіть короткі таймаути для зовнішніх викликів з запасними варіантами.

3) Краші: SIGSEGV, OOM, fatal errors

Краші дають «prematurely closed» або «connection refused» залежно від часу. OOM‑kill особливо підступні, бо тихі, якщо не перевіряти логи ядра.

cr0x@server:~$ dmesg -T | tail -n 20
[Fri Dec 26 14:03:02 2025] Out of memory: Killed process 22077 (php-fpm8.2) total-vm:812340kB, anon-rss:256144kB, file-rss:0kB, shmem-rss:0kB, UID:33 pgtables:680kB oom_score_adj:0

Рішення: Якщо OOM вбив воркерів PHP — не підвищуйте max_children. Зменшіть пам’ять на запит (плагіни, налаштування OPcache), додайте RAM або перелокувати сервіси на інші машини.

4) Невірні listen/backlog і «thundering herds»

Під раптовими спайками занадто маленький backlog або жорсткі ліміти ядра можуть перетворити навантаження на відмову підключення. Часто проявляється під час трафікових сплесків, cron‑штормів або очищення кешів.

Виправлення: Переконайтесь, що listen backlog у FPM достатній, а системні налаштування ядра не застарілі. Але знову: backlog — не заміна ємності.

WordPress та підозрілі плагіни/теми

Сам WordPress зазвичай у поряді. WordPress разом із екосистемою плагінів — як жвавий ринок, де якість різна. 502 часто викликають:

  • Зловживання admin‑ajax.php: часті опитування, довгі дії, необмежені цикли.
  • Шторм wp‑cron.php: «псевдо‑cron», що запускається веб‑запитами, може накопичуватися при навантаженні або після періоду тиші.
  • Зовнішні API‑виклики: маркетинг, CRM, платежі, доставка, аналітика. Виклики без жорстких таймаутів блокують воркери.
  • Розростання autoload опцій: великі серіалізовані бінари, що підвантажуються на кожен запит. Це як тягнути весь горище на кожну зустріч.
  • Обробка зображень під час запиту: зміни роздільної здатності і оптимізація виконуються синхронно у PHP.

Використовуйте slowlog як детектор плагінів

Стек‑трейс у slowlog — ваш друг, бо він вказує на шлях файлу під wp-content/plugins/ або тему. Це доказ. Він змінює тон розмови.

WP‑CLI‑тріаж без героїзму

Якщо сайт «плавиться» і треба швидко ізолювати проблему — вимкніть непотрібні плагіни контрольовано. Краще робити це на staging. Але в реальних інцидентах іноді доводиться відключити плагін і потім вибачатися.

cr0x@server:~$ cd /var/www/example.com/public
cr0x@server:~$ sudo -u www-data wp plugin list --status=active
+-----------------------+----------+--------+---------+
| name                  | status   | update | version |
+-----------------------+----------+--------+---------+
| woocommerce           | active   | none   | 8.5.1   |
| some-plugin           | active   | none   | 2.9.0   |
| cache-plugin          | active   | none   | 1.3.2   |
+-----------------------+----------+--------+---------+

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

Cloudflare: коли «вони», а коли — ви

Cloudflare — це реверс‑проксі з власними уявленнями. Він застосовує таймаути і з радістю поверне помилку, поки ваш origin ще «думає». Поширені причини 502, пов’язані з Cloudflare:

  • Перевантаження origin: Cloudflare робить ваш сайт більш доступним і іноді підвищує конкуренцію. Origin все ще потребує ємності.
  • Невідповідність режиму TLS: «Full» vs «Full (strict)» і проблеми із валідацією сертифікатів можуть проявлятись як проблеми з підключенням.
  • Бійка WAF і ботів: заблоковані запити можуть виглядати як часткові відмови, якщо зачеплені лише деякі endpoint‑и/користувачі.
  • Помилки allowlist IP: фаєрвол дозволяє старі діапазони Cloudflare.

Практичний підхід:

  1. Доведіть здоров’я origin, обійшовши Cloudflare (Завдання 2).
  2. Перевірте логи origin на IP Cloudflare і шаблони запитів у часи помилок.
  3. Переконайтесь, що фаєрвол не блокує edge IP (Завдання 15).

Жарт #2: Cloudflare не «падає», він просто практикує встановлення меж з вашим origin‑сервером.

Типові помилки: симптом → корінь → виправлення

1) Симптом: сплески 502 під час піків трафіку

Корінь: PHP‑FPM max_children reached; запити чекають; Nginx таймаутить, очікуючи заголовків.

Виправлення: Заміряйте RSS воркерів, встановіть max_children на основі RAM, увімкніть slowlog, виправте повільні endpoint‑и, додайте кешування. Якщо потрібно — збільшіть ресурси (більше CPU/RAM, відокремлена БД).

2) Симптом: 502 тільки для wp‑admin або admin‑ajax

Корінь: Плагін робить повільні зовнішні виклики або адмін‑endpoint обходить кеш і виконує важчі запити.

Виправлення: Використовуйте slowlog, ідентифікуйте файл плагіна, додайте жорсткі HTTP‑таймаути, кешуйте зовнішні відповіді або відключіть/замініть плагін.

3) Симптом: 502 після деплою/рестарту, потім «відновлюється»

Корінь: reload PHP‑FPM коротко видалив сокет; Nginx зловив зміну; або прогрів OPcache викликав CPU‑пік.

Виправлення: Використовуйте graceful‑reload, рознесіть рестарти, тримайте health checks і не рестартуйте Nginx і FPM одночасно. Розгляньте попередній прогрів часто використовуваних endpoint‑ів.

4) Симптом: Cloudflare показує 502, direct‑origin ок

Корінь: Фаєрвол блокує деякі IP Cloudflare; переривчасті маршрути edge→origin; невідповідний режим TLS.

Виправлення: Виправте allowlist, підтвердіть TLS‑налаштування, переконайтесь, що origin витримує конкурування Cloudflare. Перевірте через --resolve.

5) Симптом: лог Nginx показує Permission denied на FPM‑сокет

Корінь: Власник сокета не співпадає з користувачем Nginx; SELinux контекст не той.

Виправлення: Узгодьте користувачів/групи або встановіть listen.owner/listen.group/listen.mode. Для SELinux застосуйте коректну політику/контекст (не відключайте SELinux у продакшені, якщо вам не подобаються аудити).

6) Симптом: 502 з «upstream prematurely closed connection»

Корінь: Краш PHP (segfault) або OOM‑kill; іноді fatal‑помилки з термінацією воркера.

Виправлення: Перевірте journal і dmesg. Відкат недавно доданих PHP‑розширень або змін. Зменшіть тиск пам’яті. Додайте swap лише як останній засіб і не плутайте його з вирішенням проблеми.

7) Симптом: 502 після ввімкнення «плагіна продуктивності»

Корінь: Агресивний плагін кешування спричиняє cache stampede, надто часто очищає кеш або підвищує навантаження на admin‑ajax; іноді неправильно налаштовує заголовки/буферизацію.

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

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

Контрольний список A: Перші 10 хвилин на виклику

  1. Перевірте, чи помилка має брендування Cloudflare або походить від origin.
  2. Запустіть curl -D -, щоб зафіксувати заголовки і підтвердити server header.
  3. Обійдіть Cloudflare за допомогою curl --resolve, щоб протестувати origin напряму.
  4. Хвостіть лог помилок Nginx і шукайте upstream рядок помилки.
  5. Перевірте стан PHP‑FPM і journal на предмет max_children, крашів і помилок pool.
  6. Швидка системна діагностика: CPU, RAM, IO wait, заповнений диск.
  7. За потреби застосуйте міру пом’якшення: тимчасово відключіть підозрілий плагін, обмежте частоту зловісних endpoint‑ів або підвищте таймаути лише як тимчасовий крок.

Контрольний список B: Визначити, це підключення, ємність чи латентність

  • Підключення: відсутній сокет, Permission denied, відмови підключення, блокування фаєрволом. Спочатку виправляйте конфіг та політику безпеки.
  • Ємність: max_children reached, CPU завантажений, тиск пам’яті. Виправляйте шляхом підбору розмірів, кешування, більше обчислень.
  • Латентність: slowlog вказує на БД/API/плагін. Виправляйте повільні шляхи та залежності.

Контрольний список C: Посилення, що запобігає повторенню

  1. Увімкніть PHP‑FPM slowlog з низьким порогом (наприклад, 3–5s) і обертайте логи.
  2. Додайте request‑ID в access‑логи Nginx і поширюйте його (щоб можна було трасувати).
  3. Встановіть жорсткі таймаути для зовнішніх викликів у коді додатка (плагіни/теми).
  4. Розділяйте обов’язки: відмовтеся від БД на веб‑сервері для зайнятих сайтів; ізолюйте адмін‑трафік у окремий пул.
  5. Впровадьте кешування навмисно: page cache, object cache і правильну стратегію інвалідації кешу.
  6. Плануйте ємність на основі вимірювань, а не відчуттів.

Три міні‑історії з реального життя (реалістично і болісно)

Міні‑історія 1: Інцидент через хибне припущення

Сайт був за Cloudflare, і команда припустила, що це «захищає» origin від сплесків трафіку. Маркетинг запустив кампанію. Cloudflare справився: прийняв потік з’єднань і намагався звертатися до origin для cache miss.

Origin, одиночна VM, мав PHP‑FPM з консервативним pm.max_children. Під навантаженням воркери наситилися. Запити стали чекати. Nginx почав логувати upstream timed out while reading response header. Cloudflare почав повертати 502, бо не отримував відповідей вчасно.

Початковий дзвінок був типовим: «Cloudflare впав». Це не так. Edge точно повідомляв, що origin не встигав.

Що виправило проблему — не геройський тикет до Cloudflare. Команда увімкнула PHP‑FPM slowlog і знайшла endpoint, що домінував: admin-ajax.php виклики від віджета плагіна на фронтенді. Віджет звертався до зовнішнього API на кожному перегляді сторінки без кешу.

Вони кешували відповідь API, встановили короткий таймаут і зменшили частоту викликів віджета. Потім підібрали FPM на основі вимірювань пам’яті. Припущення померло; сайт вижив.

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

Доброзичливий інженер «оптимізував» стек, драматично підвищивши pm.max_children. Мета була проста: більше воркерів, менше черг, менше 502.

Це спрацювало близько години. Потім машина почала свапити. MySQL уповільнився. IO wait піднявся. PHP воркери стали довше виконуватись, а не швидше. Nginx таймаути зросли. Cloudflare‑помилки послідували. Інцидент став гіршим, бо тепер кожен запит конкурував за диск і пам’ять.

Постмортем був повчальний: вони ставилися до PHP‑FPM як до thread‑pool. Він не такий. Кожен воркер — процес з реальною вартістю пам’яті. Підвищення конкуренції без ресурсів часто збільшує контенцію і tail‑латентність.

Вирішення було нудним: обмежити воркерів до того, що RAM може витримати, винести MySQL на окремий хост і додати кеш, щоб менше запитів йшло в PHP. «Оптимізацію» відкотили, і всі тихо погодились не налаштовувати продакшен під впливом кофеїну й надії.

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

Інша компанія мала звичку: кожен інцидент починався з кореляції логів, а не зі спекуляцій. У них була маленька, але корисна конвенція: кожен рядок access‑логу Nginx містив request‑ID, і цей ID передавався до PHP через fastcgi‑param і логувався в додатку.

Коли 502 почалися, вони взяли вибірку неуспішних запитів і швидко побачили той самий шлях: endpoint checkout, що тригерив lookup тарифів доставки. Slowlog PHP‑FPM показав стек викликів; request‑ID збігався з записом Nginx; таймінги сходилися.

Вони не сперечалися про Cloudflare, Nginx чи PHP‑FPM. Не потрібно було. Ланцюжок доказів був чистим. Зовнішній API доставки був повільний і часом зависав. Код мав щедрий таймаут і не мав fallback.

Оскільки вони практикували цю нудну дисципліну, пом’якшення зайняло небагато часу: знизити таймаут, додати кешований fallback тарифів і деградуватися граціозно. Інцидент закінчився без драм, а це найкращий вид інциденту.

Оперативні рекомендації, що витримують тиск

Таймаути: використовуйте їх як обмежувачі, а не стиль життя

Усі шари мають таймаути: Cloudflare, браузер, Nginx, FastCGI, виконання PHP, зовнішні API, база даних. 502 часто — це політика таймауту в дії. Якщо ви лише підвищуєте таймаути, ви зазвичай перетворюєте швидку помилку на повільну. Користувачі все одно програють; ви лише марнуєте більше ресурсів.

Встановлюйте таймаути так, щоб:

  • зовнішні виклики падали швидко (секунди, не хвилини),
  • ваш upstream‑таймаут був трохи вище за 95‑й процентиль для законних повільних запитів, і
  • додаток деградував граціозно, коли залежності поводяться неправильно.

Цитата, яку варто тримати в голові

Парафразована ідея, приписують John Allspaw: «Безвинуватність допомагає виявити справжні причини, через які системи падають.»

FAQ

1) Чому іноді я отримую 502, а іноді 200?

Перемінні 502 зазвичай означають насичення або нестабільність. Насичення: пул воркерів іноді повний. Нестабільність: reload/краш PHP‑FPM, OOM‑kill або ненадійні залежності.

2) Чи завжди 502 — це провина PHP‑FPM?

Ні. 502 створюється шлюзом (Cloudflare/Nginx), коли upstream не вдається. Upstream може бути PHP‑FPM, але також може бути misconfigured upstream‑адреса або сам origin з боку Cloudflare.

3) Який найшвидший спосіб зрозуміти, чи задіяний Cloudflare?

Перевірте заголовки: server: cloudflare і cf-ray. Потім обійдіть Cloudflare за допомогою curl --resolve, щоб протестувати origin напряму.

4) Чи варто переходити з UNIX‑сокета на TCP для Nginx→PHP‑FPM?

Якщо ви боретеся з правами та інструментами деплою — TCP може бути простішим для розуміння. UNIX‑сокети ок і трохи ефективніші, але справжня вигода — у надійності і прозорості, а не мікро‑оптимізаціях.

5) Чому wp‑cron.php корелюється з 502?

Бо WP‑Cron запускає заплановані задачі на звичайних веб‑запитах. За певних патернів трафіку задачі накопичуються. Якщо задачі важкі (розсилки, синхронізація з API) — вони фіксують воркери і викликають черги.

6) Чи може лише кешування усунути 502?

Воно може суттєво зменшити ймовірність, знизивши навантаження на origin. Але якщо незакешовані endpoint‑и все ще повільні або пул воркерів нестабільний — кеш не врятує адмінку, checkout і API‑endpoint‑и.

7) Я бачу «max_children reached», але немає 502. Чи це важливо?

Так. Це раннє попередження. Ви можете ховати проблему щедрими таймаутами або низьким трафіком. Коли трафік підскочить або залежність уповільниться — заплатите за це.

8) Чому я отримую 502 тільки при великих завантаженнях або обробці зображень?

Завантаження та обробка зображень важкі по IO і CPU. Якщо час виконання PHP високий або тимчасовий диск заповнений/повільний — воркери зависають. Вирішіть, перемістивши обробку поза запит, збільшіть ресурси і забезпечте вільне та продуктивне сховище.

9) Якщо Nginx каже «upstream timed out», але PHP‑FPM здається idle — що робити?

«Idle» може бути оманливим: воркери можуть застрягати в невідривному IO або вузьке місце може бути в іншому місці (блокування БД, зависання DNS, зовнішні виклики). Використайте slowlog і перевірте processlist MySQL та system IO wait.

10) Чи перезапускати PHP‑FPM при 502?

Перезапуск може бути тимчасовою мірою, якщо FPM завис, але він руйнує докази. Спочатку збирайте логи, фіксуйте стан (статус служби, логи помилок, slowlog), а потім перезапускайте за потреби.

Висновок: наступні кроки, що запобігають повторенню

502 не таємничі. Вони конкретні: проксі не отримав коректної відповіді від upstream. Ваша робота — назвати зламану передачу і виправити причину її відмови.

Наступні кроки, які я справді робив би в продакшені:

  1. Інструментуйте для доказів: увімкніть PHP‑FPM slowlog, тримайте логи Nginx чистими та обертайте, логуйте request‑ID.
  2. Побудуйте швидкий триаж: заголовки → обхід edge → Nginx upstream error → здоров’я FPM → системний тиск → slowlog.
  3. Виправте справжнє вузьке місце: повільні шляхи плагінів, затримки в базі, тиск на диск або нестабільні розширення PHP.
  4. Налаштовуйте у кінці, на основі вимірювань: встановлюйте воркерів FPM на основі RSS і CPU, а не надії.
  5. Зробіть відмову безпечною: жорсткі таймаути зовнішніх API, кешування, граціозне деградування і розумні обмеження частоти на проблемні endpoint‑и.
← Попередня
Ubuntu 24.04: CIFS болісно повільний — опції монтування, які зазвичай виправляють пропускну здатність (випадок №22)
Наступна →
Debian 13: APT ламається («незадоволені залежності») — виправте це без перевстановлення

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