Ошибка 503 Service Unavailable: почему появляется и как убрать
Ошибка 503 Service Unavailable: что делать — как по заголовкам и телу ответа понять, кто отдал 503 (nginx, Apache, CMS, Cloudflare), что искать в логах и как убрать.
Ошибка 503 Service Unavailable означает, что сервер жив и понял запрос, но обслуживать его сейчас отказывается — временно. По RFC 9110 у кода две законные причины: перегрузка и плановое обслуживание. На практике 503 отдают разные слои, и чинится каждый по-своему: лимиты nginx (limit_req, limit_conn), Apache, который не смог соединиться с php-fpm, режим обслуживания CMS (WordPress, Битрикс), Cloudflare с включённой защитой, балансировщик без живых бэкендов и заглушка виртуального хостинга при превышении лимита процессов.
Поэтому первый шаг — не перезапуск сервера, а один запрос curl и взгляд на заголовок Server, Retry-After и тело ответа: они говорят, кто сформировал 503, а дальше остаётся открыть нужный лог. Если сайт чужой, можно проверить его с точек в России и Европе и убедиться, что 503 видят все, а не только вы из-за лимита на ваш IP.
Кто отдал 503: читаем заголовки и тело ответа
Запрос без кеша браузера и расширений — заголовки и начало тела:
curl -s -o /tmp/body.html -D - -A 'Mozilla/5.0' https://example.ru/ && head -c 600 /tmp/body.html
Без консоли то же самое покажет проверка HTTP-заголовков: код ответа, Server, Retry-After и цепочка редиректов.
У каждого источника 503 свой почерк:
| Кто отдал | Что в ответе | Что это значит |
|---|---|---|
| nginx | Server: nginx, тело 503 Service Temporarily Unavailable с подписью nginx | сработал limit_req/limit_conn или return 503 в конфиге |
| Apache | Server: Apache, тело Service Unavailable … due to maintenance downtime or capacity problems | mod_proxy не достучался до php-fpm или бэкенда |
| WordPress | Retry-After: 600, тело Briefly unavailable for scheduled maintenance | файл .maintenance в корне сайта |
| Varnish | тело Error 503 Backend fetch failed, Guru Meditation, XID | кеш жив, бэкенд за ним не ответил |
| HAProxy | тело No server is available to handle this request | ни один бэкенд не прошёл health-check |
| Cloudflare | cf-mitigated: challenge, страница с Ray ID и логотипом; Server: cloudflare стоит и на 503 от origin | либо origin сам отдал 503, либо это страница проверки браузера |
| Хостинг | страница панели, Resource Limit Is Reached | превышен лимит процессов или CPU на аккаунте |
Заголовок Retry-After (секунды или дата, RFC 9110 §10.2.3) ставят только те, кто отдаёт 503 осознанно: CMS в режиме обслуживания, ваш return 503. Лимиты nginx и mod_proxy его не добавляют, так что Retry-After — признак планового режима, его отсутствие — перегрузки или обрыва между слоями. Google считает 503 временной проблемой, но если она держится днями, страницы выпадают из индекса.
Если ответ пришёл от Cloudflare или другого прокси, повторите запрос напрямую к серверу, минуя его — так видно, отдаёт ли 503 origin или страница появилась уже на стороне CDN:
curl -sI --resolve example.ru:443:203.0.113.10 https://example.ru/
Чем 503 отличается от 502 и 504
502 Bad Gateway — прокси соединился с бэкендом и получил обрыв или мусор вместо HTTP-ответа: упавший или перезапускающийся php-fpm, порт, который никто не слушает, заголовки длиннее буфера. 504 Gateway Timeout — соединение есть, бэкенд принял запрос и молчит дольше proxy_read_timeout или fastcgi_read_timeout: медленный SQL, внешний API без таймаута. Как читать соответствующие строки error.log и что менять — в статье про 502 Bad Gateway.
503 отличается тем, что отказ сформулирован: сервер, прокси или CMS сказали «сейчас не могу», а не «мне не ответили». Отсюда правило: при 502 и 504 идёте в лог того, кто стоит за прокси; при 503 сначала выясняете, кто его отдал, потому что это может быть любой слой от Cloudflare до файла .maintenance. Один и тот же перегруженный php-fpm под nginx даст 502 (Resource temporarily unavailable) или 504 (запрос дождался таймаута в очереди), а под Apache — 503. Код зависит от прокси, а не от причины.
nginx: limit_req, limit_conn и режим обслуживания
nginx сам отдаёт 503 в трёх случаях: клиент превысил limit_req или limit_conn, либо в конфиге написано return 503. Для лимитов в error.log будет строка:
2026/09/16 14:02:11 [error] 1234#1234: *5678 limiting requests, excess: 12.480 by zone "perip", client: 198.51.100.7, server: example.ru, request: "GET /catalog/?page=3 HTTP/2.0"
excess — на сколько запросов клиент вылез за rate; строка появляется, когда excess превысил burst. Если такие строки массово идут с IP поисковых роботов или NAT офиса — лимит слишком жёсткий или считается не по тому ключу: типичная ошибка — $binary_remote_addr за прокси, когда все клиенты приходят с одного IP балансировщика (реальный адрес восстанавливает модуль realip).
По умолчанию превышение лимита отдаёт именно 503 (limit_req_status), и это неудачный дефолт: клиент и мониторинг не отличают «вас притормозили» от «сервер лёг». Правильный код для лимита — 429:
limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
limit_req_status 429;
limit_conn_status 429;
limit_req_log_level warn;
server {
location /search/ {
limit_req zone=perip burst=20 nodelay;
}
}
burst=20 nodelay пропускает всплеск из 20 запросов и режет только то, что сверх него. После переключения на 429 оставшиеся 503 в access.log — уже не про лимиты.
Третий случай — плановый режим обслуживания. Вариант, который включается и выключается без перезагрузки nginx — файл-флаг:
server {
error_page 503 @maintenance;
if (-f $document_root/maintenance.on) {
return 503;
}
location @maintenance {
root /var/www/maintenance;
rewrite ^ /index.html break;
add_header Retry-After 600 always;
}
}
Включение — touch maintenance.on в корне сайта, выключение — rm. Слово always обязательно: без него add_header работает только для успешных ответов и редиректов, и Retry-After в 503 не попадёт.
Apache и php-fpm: MaxRequestWorkers, backend и listen.backlog
С Apache есть два сценария, и их путают. Первый: исчерпан MaxRequestWorkers. Вопреки ожиданиям, 503 при этом не будет — новые соединения встают в очередь ядра (ListenBacklog, по умолчанию 511) и висят до таймаута, а в error_log появляется:
[mpm_prefork:error] [pid 812] AH00161: server reached MaxRequestWorkers setting, consider raising the MaxRequestWorkers setting
У MPM worker то же сообщение идёт с кодом AH00286, у event — AH00484. Если видите эту строку, а клиенты видят 503, значит его отдаёт балансировщик перед Apache (HAProxy, облачный LB), у которого все инстансы провалили health-check. nginx перед Apache в той же ситуации отдаст 502 или 504 — см. статью про 502.
Второй сценарий — Apache с php-fpm через mod_proxy_fcgi. Здесь 503 настоящий: когда php-fpm не принимает соединение, Apache пишет
[proxy:error] [pid 903] (111)Connection refused: AH02454: FCGI: attempt to connect to Unix domain socket /run/php/php8.2-fpm.sock (*) failed
[proxy_fcgi:error] [pid 903] [client 198.51.100.7:51234] AH01079: failed to make connection to backend: httpd-UDS
и отдаёт 503 — там, где nginx в той же ситуации отдал бы 502. Хуже другое: после первой неудачи mod_proxy помечает бэкенд нерабочим на retry секунд (по умолчанию 60) и отдаёт 503 всем, даже если php-fpm поднялся через секунду; в логе это AH00940: FCGI: disabled connection for (...). Для единственного локального бэкенда повторные попытки нужны сразу:
ProxyPassMatch "^/(.*\.php(/.*)?)$" "unix:/run/php/php8.2-fpm.sock|fcgi://localhost/var/www/example.ru/$1" retry=0
Почему живой php-fpm не принимает соединения? Все воркеры заняты, а очередь ожидания на сокете заполнена. Её длину задаёт listen.backlog в www.conf, а сверху ограничивает ядро через net.core.somaxconn (128 на старых ядрах, 4096 начиная с 5.4). Переполнение видно прямо на сокете:
ss -lx | grep fpm # Recv-Q — сколько ждёт, Send-Q — размер очереди
Если Recv-Q регулярно упирается в Send-Q, поднимите listen.backlog = 1024 и net.core.somaxconn = 4096 — воркеры получат шанс разгрести всплеск вместо мгновенного отказа. Но очередь лечит только короткие пики: при постоянной нехватке воркеров запросы дождутся таймаута и станут 504. Расчёт pm.max_children от памяти разобран в статье про 502.
Режим обслуживания CMS: WordPress и Битрикс
WordPress при обновлении ядра, тем и плагинов кладёт в корень сайта файл .maintenance и на каждый запрос отвечает 503 с Retry-After: 600 и текстом «Briefly unavailable for scheduled maintenance». Внутри файла — метка времени; если с неё прошло больше 10 минут, WordPress файл игнорирует и сайт открывается сам. «Застрявший» 503 после обновления — либо обновление прервалось меньше десяти минут назад, либо метка времени в будущем из-за сбоя часов сервера. Решение одно:
cat /var/www/example.ru/.maintenance # <?php $upgrading = 1758020531; ?>
rm /var/www/example.ru/.maintenance
В Битриксе аналог — параметр «Закрыть публичную часть сайта» в настройках главного модуля. Посетителям показывается site_closed.php; код ответа зависит от версии и от того, переопределена ли страница в /bitrix/php_interface/include/. Если по curl -sI заглушка уходит с кодом 200, добавьте в её копию первой строкой header('HTTP/1.1 503 Service Unavailable'); header('Retry-After: 600'); — иначе поисковики проиндексируют «сайт временно закрыт» как содержимое главной.
Cloudflare, балансировщики и заглушки хостинга
Cloudflare сам отдаёт 503 в основном в одном случае — страница проверки браузера в режиме «Under Attack» или при JavaScript-челлендже (Managed Challenge и интерактивная проверка отдают 403): посетитель с браузером проходит её незаметно, а curl, робот мониторинга и парсер получают 503 со страницей Cloudflare. Отличить любую страницу проверки от 503, пришедшего с origin, проще по заголовку cf-mitigated: challenge — он есть только у челленджа. Второй вариант — origin отдал 503, и Cloudflare его пропустил; в теле будет страница вашего nginx или WordPress. Ошибки соединения с origin у Cloudflare имеют свои коды и с 503 не смешиваются: 521 — сервер отказал в соединении (не слушает 443 или фаервол режет IP Cloudflare), 522 — таймаут соединения, 524 — origin принял запрос и не ответил за 100 секунд; список — в документации Cloudflare. Если 503 видит только робот, а браузер открывает сайт, дело в челлендже: добавьте IP роботов в правило Skip для WAF; адреса точек Нотифарио выдают по запросу — порядок описан на странице о роботе.
Балансировщик отдаёт 503, когда ему некому передать запрос: HAProxy пишет No server is available to handle this request, ingress-nginx в Kubernetes — 503 Service Temporarily Unavailable, когда у Service нет ни одного endpoint. Причина чаще в health-check, чем в бэкендах: проверка ходит на путь с 401 или редиректом либо на порт, который приложение не слушает, и все инстансы помечены down при живом приложении:
kubectl get endpoints my-app -n prod # пустой ENDPOINTS — вот и 503
На виртуальном хостинге 503 (или 508 Resource Limit Is Reached) отдаёт модуль ограничения ресурсов, когда аккаунт превысил лимит одновременных процессов или CPU. В логах сайта пусто, причина видна в статистике ресурсов панели; обычно это крон-задача, импорт из 1С или робот, обходящий фильтры каталога.
Перегрузка: кеш, лимиты, очередь
Если источник 503 — нехватка ресурсов, а не сломанный конфиг, порядок один. Сначала — кто создаёт нагрузку в момент ошибок:
grep ' 503 ' /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -5
grep ' 503 ' /var/log/nginx/access.log | awk -F'"' '{print $6}' | sort | uniq -c | sort -rn | head -5
Первая команда — топ IP, вторая — топ User-Agent. Нередко там один агрессивный робот, и вопрос закрывается limit_req с 429 для него, а не расширением сервера.
Дальше — кеш в nginx для страниц, не зависящих от сессии. Ключевая директива против 503 — fastcgi_cache_use_stale: пока бэкенд перегружен, посетители получают устаревшую копию вместо ошибки, а fastcgi_cache_lock не пускает сотню одновременных запросов к одной странице разом в php-fpm:
fastcgi_cache_path /var/cache/nginx/fcgi levels=1:2 keys_zone=site:50m inactive=1h max_size=2g;
set $skip_cache 0;
if ($http_cookie ~* "wordpress_logged_in|wp-postpass|woocommerce_items_in_cart") { set $skip_cache 1; }
location ~ \.php$ {
fastcgi_cache site;
fastcgi_cache_valid 200 301 10m;
fastcgi_cache_use_stale error timeout updating http_500 http_503;
fastcgi_cache_lock on;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
Для proxy_pass те же директивы называются proxy_cache_*. Только третьим шагом — очередь и воркеры: listen.backlog и somaxconn для сглаживания пиков, pm.max_children по расчёту от памяти. Воркеры без кеша и лимитов упираются в память и переносят проблему в базу.
Как узнать о 503 раньше клиентов
503 часто не для всех: лимит срабатывает по одному IP, челлендж Cloudflare — только для роботов, хостинг-заглушка — в обеденный пик, режим обслуживания — на десять минут после ночного автообновления. Владелец, открыв сайт днём из офиса, видит 200 и узнаёт о проблеме от клиента.
Такие случаи ловит внешняя проверка с ожидаемым кодом 200 и ключевой фразой, которая есть только на рабочей странице (название товара, «Корзина», подпись в футере): заглушки хостинга и Битрикса нередко приходят с кодом 200, и проверка только по коду их пропустит. Интервал — 1–2 минуты для главной и одной тяжёлой страницы (поиск, фильтр каталога): они первыми упираются в лимиты. Так устроен мониторинг доступности сайта: сбой засчитывается после подтверждения с нескольких точек (число задаётся в настройках монитора), поэтому при подтверждении от двух и более точек одиночный 429 из-за лимита на IP одной точки тревогой не станет. Если сайт за Cloudflare или WAF, IP точек нужно внести в белый список, иначе мониторинг будет честно сообщать о 503 с челленджа. И плановые работы под мониторингом должны выглядеть правильно: заглушка отдаёт 503 с Retry-After, а не 200 — тогда простой попадёт в историю инцидентов даже у монитора без проверки по фразе, а поисковики не проиндексируют заглушку.
Коротко
- 503 — «сервер жив, но временно не обслуживает». Первым делом
curl -s -D -: подпись в теле у nginx, Apache, WordPress, Varnish, HAProxy, Cloudflare и хостинга своя, аRetry-Afterесть только у осознанного 503 (CMS,return 503). - В nginx переключите
limit_req_statusиlimit_conn_statusна 429 — оставшиеся 503 в логе будут настоящими. Режим обслуживания — файл-флаг иadd_header Retry-After 600 always. - Apache + php-fpm: 503 при
AH01079, бэкенд отключается наretry=60секунд; для локального php-fpm ставьтеretry=0. Очередь сокета —listen.backlogиnet.core.somaxconn, смотретьss -lx. - WordPress: удалить
.maintenance; Битрикс: проверить «Закрыть публичную часть» и код ответа заглушки. - При перегрузке: источник нагрузки в
access.log→fastcgi_cache_use_stale … http_503→ лимиты → и только потом воркеры. - Мониторинг с ожидаемым кодом 200 и ключевой фразой с нескольких точек ловит 503, который «не для всех».
Вопросы и ответы
Ошибка 503 — это проблема на моём компьютере или на сайте?
Сколько ждать, если сайт отдаёт 503?
Почему 503 появляется только под нагрузкой и сам проходит?
Что делать, если WordPress завис в режиме обслуживания с ошибкой 503?
Как отдать 503 на время работ, чтобы не потерять позиции в поиске?
Что означает 503 в мониторинге, если сайт открывается в браузере?
Не проверяйте вручную — следите автоматически
Нотифарио проверит сайт с нескольких точек и пришлёт уведомление в Telegram, MAX, по SMS или звонком, когда что-то сломается. Оплата только за проверки, 50 ₽ на старт.
Подключить бесплатно Проверить сайт сейчас