Что делать, если

Ошибка 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 свой почерк:

Кто отдалЧто в ответеЧто это значит
nginxServer: nginx, тело 503 Service Temporarily Unavailable с подписью nginxсработал limit_req/limit_conn или return 503 в конфиге
ApacheServer: Apache, тело Service Unavailable … due to maintenance downtime or capacity problemsmod_proxy не достучался до php-fpm или бэкенда
WordPressRetry-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
Cloudflarecf-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.logfastcgi_cache_use_stale … http_503 → лимиты → и только потом воркеры.
  • Мониторинг с ожидаемым кодом 200 и ключевой фразой с нескольких точек ловит 503, который «не для всех».

Вопросы и ответы

Ошибка 503 — это проблема на моём компьютере или на сайте?
На сайте. 503 формирует сервер, прокси или CMS, когда временно не может обслужить запрос. Очистка кеша и смена браузера не помогут; исключение — лимит на ваш IP или проверка браузера Cloudflare, тогда с другого адреса или из обычного браузера сайт откроется.
Сколько ждать, если сайт отдаёт 503?
Если в ответе есть заголовок Retry-After, там указано число секунд или время, когда сервер ждёт повторного запроса. Если заголовка нет, это обычно перегрузка: попробуйте через несколько минут, а владельцу сайта стоит смотреть логи, а не ждать.
Почему 503 появляется только под нагрузкой и сам проходит?
Заканчиваются воркеры php-fpm или Apache и переполняется очередь на сокете, либо срабатывает лимит хостинга на число процессов. Помогают кеш страниц в nginx с fastcgi_cache_use_stale, лимит запросов на тяжёлые адреса и увеличение listen.backlog; расширять число воркеров стоит только после этого.
Что делать, если WordPress завис в режиме обслуживания с ошибкой 503?
Удалите файл .maintenance в корне сайта. WordPress создаёт его на время обновления и сам игнорирует через 10 минут, но если обновление прервалось или метка времени в файле некорректна, сайт остаётся закрытым. После удаления проверьте, что обновлявшийся плагин активируется без ошибок.
Как отдать 503 на время работ, чтобы не потерять позиции в поиске?
Отдавайте именно 503, а не 200 с текстом заглушки, и добавьте заголовок Retry-After. Поисковики трактуют такой ответ как временный и возвращаются позже. Работы дольше нескольких дней лучше не прикрывать 503 — страницы могут выпасть из индекса.
Что означает 503 в мониторинге, если сайт открывается в браузере?
Скорее всего роботу отдаёт 503 защита от ботов (Cloudflare, WAF) или лимит запросов по IP. Добавьте адреса точек мониторинга в белый список и проверяйте страницу по ключевой фразе, чтобы отличать страницу защиты от рабочего сайта.

Не проверяйте вручную — следите автоматически

Нотифарио проверит сайт с нескольких точек и пришлёт уведомление в Telegram, MAX, по SMS или звонком, когда что-то сломается. Оплата только за проверки, 50 ₽ на старт.

Подключить бесплатно Проверить сайт сейчас