Новинка Виртуальный VDS сервер в Нидерландах от 390р
Выберите продукт

Nginx maintenance mode на VDS: 503, Retry-After, whitelist и health checks

Разбираем безопасный режим обслуживания в Nginx для VDS: как включать 503 без поломки SSL-обновлений, пропускать админов по whitelist, не путать мониторинг и health checks, а также быстро включать и снимать заглушку во время работ.
Nginx maintenance mode на VDS: 503, Retry-After, whitelist и health checks

Режим обслуживания кажется простой задачей: положить красивую страницу «мы скоро вернёмся» и на время работ закрыть сайт. На практике у администраторов быстро появляются нюансы: поисковикам нужно отдать именно 503, браузерам и CDN нельзя закешировать заглушку надолго, выпуск и продление сертификатов через ACME challenge не должны сломаться, а мониторинг и балансировщик должны понимать, что происходит с узлом.

В этой инструкции соберём рабочий вариант nginx maintenance mode для VDS: управляемый флаг-файлом, с кодом 503 Service Unavailable, заголовком Retry-After, whitelist для администраторов и отдельными health checks. Конфигурация рассчитана на обычный Nginx как фронтенд перед PHP-FPM, Node.js, Python-приложением или другим upstream.

Главная идея: maintenance mode не должен выглядеть как авария. Для клиентов это понятное временное окно, для поисковиков — временная недоступность, для мониторинга — контролируемое состояние, для админов — возможность проверить сайт до снятия заглушки.

Что должен делать правильный maintenance mode

Если просто заменить корень сайта статической страницей или поставить редирект на /maintenance.html, получится не совсем корректно. Редирект с кодом 302 или 200-страница обслуживания может быть воспринята роботами как нормальный контент. В худшем случае поисковик временно увидит вместо главной страницы текст заглушки и обновит сниппет.

Правильная схема для плановых работ обычно выглядит так:

  • обычным посетителям Nginx возвращает 503, а не 200;
  • в ответе есть Retry-After, чтобы клиенты и роботы понимали ожидаемую паузу;
  • заглушка не кешируется браузерами, CDN и промежуточными прокси;
  • администраторы из nginx whitelist обходят заглушку и видят реальное приложение;
  • пути /.well-known/acme-challenge/ доступны всегда, иначе автоматическое продление SSL может упасть;
  • health checks разделены по смыслу: один проверяет, жив ли Nginx, другой — готов ли узел принимать пользовательский трафик.

Разделение проверок особенно важно на VDS, где на одном сервере часто живут веб-сервер, приложение, база данных, cron-задачи и мониторинг. Если заглушка включается на время миграции базы, не всегда нужно будить дежурного из-за «падения сайта»: достаточно показать, что maintenance включён осознанно.

Подготовка файлов и каталогов

Команды ниже подходят для Debian/Ubuntu и RHEL-based систем, потому что мы создаём обычные каталоги и файлы. Отличия в пакетном менеджере нужны только если Nginx ещё не установлен.

Установка Nginx, если он ещё не установлен

Debian/Ubuntu:

sudo apt update
sudo apt install nginx
sudo systemctl enable --now nginx

AlmaLinux, Rocky Linux, CentOS Stream, Oracle Linux:

sudo dnf install nginx
sudo systemctl enable --now nginx

Теперь создадим каталог для флагов обслуживания и простую страницу. В реальном проекте вместо текстовой строки обычно кладут аккуратный HTML-файл с логотипом, временем окончания работ и понятным сообщением для пользователей.

sudo mkdir -p /etc/nginx/maintenance /var/www/maintenance
echo 'Сайт временно на обслуживании. Пожалуйста, повторите запрос позже.' | sudo tee /var/www/maintenance/503.html

Флаг будем включать командой touch, а выключать удалением файла. Для домена example.com используем имя /etc/nginx/maintenance/example.com.enabled. В бою замените домен на свой.

sudo touch /etc/nginx/maintenance/example.com.enabled
sudo rm -f /etc/nginx/maintenance/example.com.enabled

Каталоги и флаг-файл для включения maintenance mode в Nginx

Базовая конфигурация: 503 и Retry-After

Блоки geo и map должны находиться в контексте http, то есть не внутри server и не внутри location. На Debian/Ubuntu их удобно вынести в отдельный файл, который подключается из /etc/nginx/nginx.conf через каталог /etc/nginx/conf.d/. В RHEL-based системах подход такой же: главное, чтобы файл попал в http-контекст.

geo $maintenance_whitelist {
    default 0;
    127.0.0.1 1;
    203.0.113.10 1;
    203.0.113.0/24 1;
}

map $uri $maintenance_uri_allowed {
    default 0;
    ~^/\.well-known/acme-challenge/ 1;
    =/healthz 1;
    =/readyz 1;
}

map "$maintenance_flag:$maintenance_whitelist:$maintenance_uri_allowed" $maintenance_return {
    default 0;
    "1:0:0" 1;
}

Ниже — фрагмент server-блока. Его можно адаптировать под существующий виртуальный хост. Важно не копировать вслепую поверх всей конфигурации: сохраните свои listen, server_name, SSL-настройки, proxy/FastCGI location и логи.

server {
    listen 80;
    listen 443 ssl http2;
    server_name example.com www.example.com;

    set $maintenance_flag 0;

    if (-f /etc/nginx/maintenance/example.com.enabled) {
        set $maintenance_flag 1;
    }

    if ($maintenance_return) {
        return 503;
    }

    error_page 503 @maintenance;

    location @maintenance {
        internal;
        root /var/www/maintenance;
        add_header Retry-After 600 always;
        add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
        add_header Pragma "no-cache" always;
        add_header Expires "0" always;
        try_files /503.html =503;
    }

    location /.well-known/acme-challenge/ {
        root /var/www/letsencrypt;
        try_files $uri =404;
    }

    location = /healthz {
        access_log off;
        default_type text/plain;
        return 200 "ok";
    }

    location = /readyz {
        access_log off;
        default_type text/plain;
        if (-f /etc/nginx/maintenance/example.com.enabled) {
            return 503;
        }
        return 200 "ready";
    }

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

В этой схеме переменная $maintenance_flag становится равной 1, если существует флаг-файл. Переменная $maintenance_whitelist равна 1 для разрешённых IP. Переменная $maintenance_uri_allowed равна 1 для служебных путей, которые должны работать даже во время обслуживания. Итоговая переменная $maintenance_return включает 503 только при сочетании «maintenance включён, IP не в whitelist, URI не служебный».

Директивы if в Nginx часто ругают, и не без причины: внутри location они могут вести себя неожиданно, если использовать сложную перепись URI. Но в таком варианте мы делаем только set и return, это безопасный и распространённый шаблон.

Retry-After: секунды или дата

Заголовок Retry-After можно задать числом секунд или HTTP-датой. Для коротких работ удобнее число:

add_header Retry-After 600 always;

Это означает «попробуйте снова через 600 секунд». Для заранее объявленного окна можно указать дату, но тут легко ошибиться с часовым поясом и форматом. Поэтому на практике для maintenance mode чаще используют секунды: 300, 600, 1800 или 3600.

Ключевое слово always важно: без него Nginx добавляет некоторые заголовки не ко всем кодам ответа. Нам нужно, чтобы Retry-After и запрет кеширования присутствовали именно на 503.

Не ставьте слишком большой Retry-After «на всякий случай». Если работы занимают 10 минут, значение на сутки будет мешать роботам и некоторым клиентам вернуться вовремя. Лучше продлить окно при необходимости и обновить конфигурацию или использовать переменную через include, если у вас строгие регламенты.

FastFox VDS
Облачный VDS-сервер
Виртуальные серверы с быстрым запуском и гибкой конфигурацией от 390₽ / мес
Доступные локации
Россия Нидерланды

Whitelist: как не закрыть доступ самим себе

В блоке geo мы перечислили IP-адреса, которым можно видеть реальный сайт во время обслуживания. Это удобно для проверки миграции, прогрева кеша, ручного smoke-теста и финального контроля перед открытием доступа для всех.

Для сопровождения удобнее держать whitelist в отдельном файле:

geo $maintenance_whitelist {
    default 0;
    include /etc/nginx/maintenance/whitelist.conf;
}

А содержимое /etc/nginx/maintenance/whitelist.conf может быть таким:

127.0.0.1 1;
203.0.113.10 1;
203.0.113.0/24 1;

После изменения whitelist проверьте и перезагрузите Nginx:

sudo nginx -t
sudo systemctl reload nginx

Если сервер стоит за CDN, внешним балансировщиком или reverse proxy, $remote_addr может быть адресом прокси, а не клиента. Тогда whitelist внезапно начнёт пропускать всех пользователей через доверенный прокси или, наоборот, не пропустит никого. В такой архитектуре сначала корректно настройте получение реального IP.

set_real_ip_from 198.51.100.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

Здесь set_real_ip_from должен содержать только адреса ваших доверенных прокси или балансировщиков. Нельзя доверять заголовку X-Forwarded-For от всего интернета: клиент может подставить туда любой IP и обойти whitelist.

ACME challenge: почему его нужно пропускать

Если сертификаты выпускаются через HTTP-01, клиент размещает проверочный файл в /.well-known/acme-challenge/, а центр сертификации обращается к вашему домену по HTTP. Если maintenance mode перехватит этот путь и вернёт 503, продление сертификата может завершиться ошибкой. Это особенно неприятно, когда обслуживание затянулось и совпало с автоматическим renewal.

Именно поэтому в map $uri $maintenance_uri_allowed путь /.well-known/acme-challenge/ помечен как разрешённый. Даже при включённой заглушке Nginx будет искать файл проверки в каталоге /var/www/letsencrypt.

Проверьте, что challenge действительно не блокируется:

sudo mkdir -p /var/www/letsencrypt/.well-known/acme-challenge
echo 'test' | sudo tee /var/www/letsencrypt/.well-known/acme-challenge/ping
curl -i http://example.com/.well-known/acme-challenge/ping

Ожидаемый результат — 200 или хотя бы не 503, если файла нет и вы получили 404. Для проверки нам важно, что запрос не попал в maintenance-заглушку.

Если вы используете коммерческие SSL-сертификаты или смешанную схему с разными центрами сертификации, всё равно полезно держать служебные пути отдельно от пользовательской заглушки: это упрощает продление, диагностику и миграции доменов.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Health checks: /healthz и /readyz — не одно и то же

Частая ошибка — использовать один URL для всех проверок. Например, внешний мониторинг, балансировщик и Kubernetes-подобная система опрашивают /health, а администратор не понимает, должен ли он возвращать 200 во время maintenance. Лучше разделить смысл.

  • /healthz — жив ли сам Nginx и отвечает ли виртуальный хост. Во время обслуживания может возвращать 200.
  • /readyz — готов ли узел принимать пользовательский трафик. Во время обслуживания обычно возвращает 503.

Такой подход удобен для балансировщика: если /readyz вернул 503, узел можно временно исключить из ротации, но при этом /healthz покажет, что веб-сервер не умер. Для одиночного VDS внешний мониторинг можно настроить по-разному: либо проверять главную страницу и учитывать maintenance-окна, либо проверять /healthz, а факт включённой заглушки контролировать отдельным алертом.

Если приложение умеет отдавать собственный readiness, можно проксировать /readyz в upstream, но добавить проверку флаг-файла перед проксированием. Тогда maintenance гарантированно выводит узел из пользовательского трафика даже если приложение технически живо.

location = /readyz {
    access_log off;
    if (-f /etc/nginx/maintenance/example.com.enabled) {
        return 503;
    }
    proxy_pass http://127.0.0.1:3000/readyz;
}

Раздельные health check endpoints для Nginx и балансировщика

Включение, выключение и проверка

После добавления конфигурации всегда начинайте с проверки синтаксиса. Nginx не применит битую конфигурацию при reload, но лучше увидеть ошибку до начала окна работ.

sudo nginx -t
sudo systemctl reload nginx

Включить maintenance:

sudo touch /etc/nginx/maintenance/example.com.enabled

Выключить maintenance:

sudo rm -f /etc/nginx/maintenance/example.com.enabled

Проверка обычного ответа:

curl -i https://example.com/

В ответе для не-whitelist IP должны быть HTTP/2 503 или HTTP/1.1 503 Service Temporarily Unavailable, Retry-After и заголовки запрета кеширования. Проверка служебных URL:

curl -i https://example.com/healthz
curl -i https://example.com/readyz
curl -i https://example.com/.well-known/acme-challenge/ping

Если вы тестируете с самого сервера, запрос может попадать в whitelist через 127.0.0.1. Это нормально, но для проверки поведения обычного посетителя используйте внешний IP, временно уберите свой адрес из whitelist или проверьте через отдельную сеть.

Логи и наблюдаемость во время обслуживания

Во время плановых работ полезно видеть, сколько пользователей упёрлось в заглушку, какие боты продолжают активно ходить по сайту и не заблокировали ли вы важные служебные пути. Можно завести отдельный access log для maintenance-location, но у named location @maintenance есть нюансы: логирование чаще удобнее оставить на уровне server, добавив в формат переменные состояния.

log_format main_ext '$remote_addr $host $request $status maintenance=$maintenance_return whitelist=$maintenance_whitelist request_time=$request_time';
access_log /var/log/nginx/example.com.access.log main_ext;

После этого в логах будет видно, какие ответы прошли через maintenance-логику. Для быстрой проверки:

sudo tail -f /var/log/nginx/example.com.access.log

Если у вас есть Prometheus-экспортёр, blackbox monitoring или внешняя система статусов, заранее договоритесь, как она интерпретирует 503 на главной странице во время плановых работ. Самый плохой вариант — включить обслуживание, получить шквал ложных алертов, отключить мониторинг вручную и забыть включить его обратно.

Типовые ошибки

Заглушка отдаётся с кодом 200

Это удобно визуально, но плохо для SEO и автоматических клиентов. Страница обслуживания должна быть телом ответа 503, а не отдельной успешной страницей. Используйте error_page 503 @maintenance и return 503.

Забыли always у add_header

Без always заголовок Retry-After может не появиться там, где вы его ждёте. Проверяйте реальный ответ через curl -i, а не только глазами в браузере.

CDN закешировал maintenance-страницу

Добавьте Cache-Control: no-store на 503 и отдельно проверьте правила CDN. Некоторые панели имеют собственные настройки кеширования ошибок. Если CDN стоит перед VDS, убедитесь, что он не превращает временную заглушку в долгоживущий объект.

Whitelist не работает за прокси

Проверьте, какой IP видит Nginx. Временно добавьте в лог $remote_addr, $realip_remote_addr и $http_x_forwarded_for. Настраивайте real_ip_header только для доверенных прокси.

ACME HTTP-01 начал падать

Убедитесь, что /.well-known/acme-challenge/ исключён из maintenance-режима и что location для challenge находится в нужном server-блоке. Если у вас несколько доменов и редирект с HTTP на HTTPS, проверьте оба сценария. При переносе сайта между серверами пригодится отдельный план: например, чеклист из статьи про миграцию сайта без простоя.

Мини-чеклист перед плановыми работами

  • Заранее добавьте и протестируйте конфигурацию на staging или в коротком тестовом окне.
  • Проверьте nginx -t и reload без ошибок.
  • Убедитесь, что ваш IP находится в whitelist и не меняется из-за VPN или провайдера.
  • Проверьте 503 и Retry-After через curl -i.
  • Проверьте доступность ACME challenge.
  • Разделите /healthz и /readyz, особенно если есть балансировщик.
  • После завершения работ удалите флаг-файл и проверьте главные пользовательские сценарии.

Такой maintenance mode хорош тем, что он прост: нет отдельного демона, нет зависимости от приложения, включение и выключение делаются атомарным созданием или удалением файла. При этом схема достаточно гибкая для VDS с несколькими сайтами, reverse proxy и автоматическим SSL. Если однажды подготовить её аккуратно, плановые обновления, миграции базы и релизы перестают быть нервным «закроем всё firewall-ом» и превращаются в управляемую эксплуатационную процедуру.

Поделиться статьей

Вам будет интересно

LVM snapshots на Linux VDS: создание, backup и rollback без лишнего риска OpenAI Статья написана AI (GPT 5)

LVM snapshots на Linux VDS: создание, backup и rollback без лишнего риска

LVM snapshots помогают быстро зафиксировать состояние тома на Linux VDS перед обновлением, миграцией или резервным копированием. Р ...
HAProxy на VDS: TLS termination, health checks, PROXY protocol и Nginx backend OpenAI Статья написана AI (GPT 5)

HAProxy на VDS: TLS termination, health checks, PROXY protocol и Nginx backend

Разберём схему, где HAProxy принимает HTTPS, завершает TLS, проверяет живость узлов и передаёт трафик на Nginx backend по PROXY pr ...
MySQL Community vs MariaDB vs Percona Server в 2026: что выбрать для VDS OpenAI Статья написана AI (GPT 5)

MySQL Community vs MariaDB vs Percona Server в 2026: что выбрать для VDS

В 2026 году выбор MySQL-совместимой СУБД для VDS уже не сводится к вопросу «что быстрее». Важнее совместимость с приложением, обно ...