Cloudflare пишет «SSL handshake failed», вы идёте проверять сертификат — а он валидный и действует ещё три месяца. Значит, ищете не там. В моей практике 525 почти никогда не был про SSL.
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.
Это значение по умолчанию в 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;
}
worker_processes auto;
worker_rlimit_nofile 16384;
events {
worker_connections 8192;
}
А вот здесь прячется грабля, из-за которой правка часто не даёт эффекта. 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
Это важно. systemctl reload nginx перечитывает конфиг, но не пересоздаёт мастер-процесс — а лимиты применяются именно при его запуске. Поэтому после правки нужен полный перезапуск, иначе всё останется как было, и вы решите, что настройка не помогла.
systemctl daemon-reload
systemctl restart nginx
cat /proc/$(cat /run/nginx.pid)/limits | grep "open files"
Отдельный сюрприз, который заставляет думать, что ничего не помогло. Cloudflare держит пул постоянных соединений к вашему серверу и какое-то время переиспользует старые, уже мёртвые. Первые несколько запросов после фикса всё ещё могут вернуть 525.
Это рассасывается само примерно за полминуты. Не откатывайте правку в этот момент — просто подождите и проверьте ещё раз.
error.log ищем worker_connections are not enough.worker_connections и worker_rlimit_nofile.LimitNOFILE — без него первое бесполезно./proc/PID/limits.proxy_pass на внешние домены, заменяя редиректом.И общий принцип, который экономит часы: когда название ошибки указывает на одно, а ведёт она себя как другое — верьте поведению. «SSL handshake failed», приходящий по расписанию нагрузки, — это не про SSL.
Cloudflare не смог установить TLS-соединение с вашим сервером. Название «SSL handshake failed» вводит в заблуждение: сертификат чаще всего в порядке, а рукопожатие не состоялось потому, что сервер не принял соединение вообще — например, исчерпал лимит одновременных подключений.
Потому что причина обычно в исчерпании ресурса, а не в поломке. Пока одновременных соединений мало, всё работает; на пике лимит упирается в потолок, и часть запросов отваливается. Отсюда ощущение, что сайт «то работает, то нет».
Значение по умолчанию в Ubuntu — 768, и для сайта с WebSocket-соединениями этого мало: каждое проксируемое соединение занимает два слота. Практичный ориентир — 4096–8192 на процесс. Одновременно нужно поднять лимит открытых файлов, иначе настройка не сработает.
Потому что systemd ограничивает процесс раньше, чем до дела доходит директива nginx. По умолчанию мягкий лимит открытых файлов — 1024, и nginx не может его превысить. Нужен файл override для сервиса с LimitNOFILE и полный перезапуск: reload не пересоздаёт мастер-процесс и новые лимиты не подхватит.
Сайт периодически отдаёт 5xx и непонятно почему? Посмотрю логи и скажу причину — до того, как речь зайдёт об оплате.
Написать в Telegram @vvs35_bot