vvs35.ru боты, парсеры, серверы
УслугиГотовые решенияБлог принимаем заказы

Ошибка 525 у Cloudflare: дело не в сертификате

Cloudflare пишет «SSL handshake failed», вы идёте проверять сертификат — а он валидный и действует ещё три месяца. Значит, ищете не там. В моей практике 525 почти никогда не был про SSL.

Что на самом деле означает 525

Cloudflare работает как посредник: принимает запрос от посетителя и сам идёт к вашему серверу. Ошибка 525 говорит, что на втором участке не удалось установить TLS-соединение.

Формулировка «SSL handshake failed» подталкивает к сертификату, но рукопожатие не состоится и в другом случае — если сервер вообще не принял соединение. Для Cloudflare это выглядит одинаково: он постучался, ответа не получил, вернул 525.

Характерный признак: 525 приходит не постоянно, а волнами. Утром сайт работает, днём отваливается, вечером снова работает. Сломанный сертификат так себя не ведёт — он ломается сразу и навсегда. А вот исчерпанный лимит соединений ведёт себя ровно так.

Куда смотреть первым делом

Не в сертификат, а в лог ошибок nginx:

tail -100 /var/log/nginx/error.log | grep -E "worker_connections|connect.*upstream"

Если увидите такую строку — причина найдена:

768 worker_connections are not enough while connecting to upstream

Это значит, что nginx упёрся в потолок одновременных соединений и перестал принимать новые. Cloudflare в этот момент получает отказ и показывает посетителю 525.

Откуда берётся число 768

Это значение по умолчанию в Ubuntu. Его никто не выбирал под вашу нагрузку — оно просто лежит в конфиге с момента установки:

events {
    worker_connections 768;
}

Для сайта-визитки этого достаточно с запасом. Проблемы начинаются там, где соединения живут долго, а не закрываются через сотню миллисекунд.

Главная ловушка: одно соединение занимает два слота

Когда nginx проксирует запрос дальше — на приложение, бота или WebSocket, — он держит два соединения одновременно: одно с клиентом, второе с апстримом. То есть реальная ёмкость вдвое меньше цифры в конфиге.

С WebSocket это особенно заметно. Обычный HTTP-запрос занимает слот на доли секунды, а WebSocket-соединение висит открытым часами. Чат, живые уведомления, панель с обновлением в реальном времени — каждая открытая вкладка держит слот, пока пользователь её не закроет.

Дальше арифметика простая: 768 слотов, из них половина уходит на апстрим — остаётся около 380 одновременных клиентов. На пике это набирается быстрее, чем кажется.

Вторая причина: реверс-прокси на внешний адрес

Отдельная история, которая усугубляет первую. Если в конфиге есть проксирование на чужой внешний домен по HTTPS:

location / {
    proxy_pass https://cdn.example.com;   // так делать не стоит
}

…то каждый такой запрос заставляет ваш nginx самому лезть в интернет и ждать ответа. Если внешний адрес отвечает медленно или не резолвится по IPv6, воркер стоит в ожидании до таймаута — а это обычно 60 секунд. Всё это время слот занят.

Cloudflare регулярно проверяет доступность origin, и каждая такая проверка съедала соединения. Лечится заменой проксирования на редирект — тогда nginx отвечает мгновенно и никуда не ходит:

location / {
    return 301 https://cdn.example.com$request_uri;
}

Как чинить

Шаг 1. Поднять лимиты в nginx

worker_processes auto;
worker_rlimit_nofile 16384;

events {
    worker_connections 8192;
}

Шаг 2. Разрешить это на уровне systemd

А вот здесь прячется грабля, из-за которой правка часто не даёт эффекта. systemd ограничивает процесс раньше, чем до дела доходит директива nginx. Мягкий лимит открытых файлов по умолчанию — 1024, и сколько ни пиши worker_rlimit_nofile 16384, реальный потолок останется прежним.

Проверить легко: посмотрите лимиты живого мастер-процесса.

cat /proc/$(cat /run/nginx.pid)/limits | grep "open files"

Если там 1024 — nginx вас не обманывает, его просто держат снаружи. Нужен override для сервиса:

# /etc/systemd/system/nginx.service.d/limits.conf
[Service]
LimitNOFILE=65536:524288

Шаг 3. Полный перезапуск, а не reload

Это важно. systemctl reload nginx перечитывает конфиг, но не пересоздаёт мастер-процесс — а лимиты применяются именно при его запуске. Поэтому после правки нужен полный перезапуск, иначе всё останется как было, и вы решите, что настройка не помогла.

systemctl daemon-reload
systemctl restart nginx
cat /proc/$(cat /run/nginx.pid)/limits | grep "open files"

Почему после починки 525 ещё немного продолжается

Отдельный сюрприз, который заставляет думать, что ничего не помогло. Cloudflare держит пул постоянных соединений к вашему серверу и какое-то время переиспользует старые, уже мёртвые. Первые несколько запросов после фикса всё ещё могут вернуть 525.

Это рассасывается само примерно за полминуты. Не откатывайте правку в этот момент — просто подождите и проверьте ещё раз.

Короткий чек-лист

  1. Ошибка приходит волнами, а не постоянно — почти наверняка это лимиты, а не сертификат.
  2. В error.log ищем worker_connections are not enough.
  3. Поднимаем worker_connections и worker_rlimit_nofile.
  4. Добавляем override systemd с LimitNOFILE — без него первое бесполезно.
  5. Делаем restart, а не reload.
  6. Проверяем реальные лимиты через /proc/PID/limits.
  7. Убираем proxy_pass на внешние домены, заменяя редиректом.
  8. Ждём полминуты, прежде чем делать выводы.

И общий принцип, который экономит часы: когда название ошибки указывает на одно, а ведёт она себя как другое — верьте поведению. «SSL handshake failed», приходящий по расписанию нагрузки, — это не про SSL.

Коротко: частые вопросы

Что означает ошибка 525 у Cloudflare?

Cloudflare не смог установить TLS-соединение с вашим сервером. Название «SSL handshake failed» вводит в заблуждение: сертификат чаще всего в порядке, а рукопожатие не состоялось потому, что сервер не принял соединение вообще — например, исчерпал лимит одновременных подключений.

Почему 525 появляется не всегда, а периодически?

Потому что причина обычно в исчерпании ресурса, а не в поломке. Пока одновременных соединений мало, всё работает; на пике лимит упирается в потолок, и часть запросов отваливается. Отсюда ощущение, что сайт «то работает, то нет».

Сколько worker_connections нужно ставить в nginx?

Значение по умолчанию в Ubuntu — 768, и для сайта с WebSocket-соединениями этого мало: каждое проксируемое соединение занимает два слота. Практичный ориентир — 4096–8192 на процесс. Одновременно нужно поднять лимит открытых файлов, иначе настройка не сработает.

Почему worker_rlimit_nofile не применяется?

Потому что systemd ограничивает процесс раньше, чем до дела доходит директива nginx. По умолчанию мягкий лимит открытых файлов — 1024, и nginx не может его превысить. Нужен файл override для сервиса с LimitNOFILE и полный перезапуск: reload не пересоздаёт мастер-процесс и новые лимиты не подхватит.

Сайт периодически отдаёт 5xx и непонятно почему? Посмотрю логи и скажу причину — до того, как речь зайдёт об оплате.

Написать в Telegram @vvs35_bot

Читать дальше