Как я диагностировал недоступность сайта через цепочку Reverse Proxy и нашёл сетевой timeout до origin
В один момент перестали нормально открываться несколько моих сайтов. Накануне всё работало, конфигурация nginx не менялась, но сайты через цепочку reverse proxy перестали отвечать.
Последовательная проверка каждого участка позволила быстро локализовать проблему между промежуточным reverse proxy и origin-серверами.
Позже дополнительная проверка показала, что причиной сбоя на этом направлении была фильтрация с использованием технических средств противодействия угрозам (ТСПУ).
Важно разделять две вещи:
- сетевые тесты и nginx-логи подтвердили
TCP connect timeoutпри соединении middle proxy → origin; - конкретная причина в виде фильтрации с использованием ТСПУ была установлена отдельно.
Все реальные домены и публичные IP-адреса в статье заменены на адреса для документации.
Используемые адреса
Код:
example.org — основной сайт
tracker.example.org — трекер
203.0.113.10 — публичный frontend
198.51.100.10 — промежуточный reverse proxy
192.0.2.10 — web origin
192.0.2.20 — tracker origin
198.51.100.1 — gateway middle proxy
Используемые IP относятся к TEST-NET диапазонам и не отражают реальную географию серверов.
Исходная схема
Код:
Пользователь
|
v
Frontend
203.0.113.10
|
v
Middle proxy
198.51.100.10
|
+----------------------+
| |
v v
192.0.2.10 192.0.2.20
Web origin Tracker origin
1. Проверяю DNS
С рабочего компьютера проверяю разрешение имени через нужный DNS-сервер:
Код:
nslookup example.org DNS_SERVER_IP
Получаю ожидаемый адрес:
Код:
example.org
Address: 203.0.113.10
Проверяемый DNS-сервер возвращает ожидаемый IP.
Это не полная проверка всей DNS-инфраструктуры, но очевидную ошибку разрешения имени через этот DNS-сервер на данном этапе исключает.
Позже
curl также показывает, что example.org разрешается в ожидаемый адрес:
Код:
Connected to example.org (203.0.113.10) port 443
2. Проверяю HTTPS
На Windows:
Код:
curl.exe -vk --max-time 15 https://example.org/ -o NUL
TCP-соединение устанавливается:
Код:
Connected to example.org (203.0.113.10) port 443
TLS-handshake завершается успешно, через ALPN в моём случае согласовывается HTTP/1.1:
Код:
ALPN: server accepted http/1.1
curl отправляет запрос:
Код:
GET / HTTP/1.1
Host: example.org
Код:
Request completely sent off
Но HTTP-ответ не приходит в течение заданного времени ожидания.
На этом этапе уже понятно:
- имя разрешяется в ожидаемый IP;
- TCP-соединение с frontend устанавливается;
- TLS работает;
- HTTP-запрос полностью отправляется на frontend;
- HTTP-ответ клиенту не возвращается в течение заданных 15 секунд.
Где именно возникает задержка, проверяю дальше.
Ключ
-k отключает обычную проверку TLS-сертификата, включая доверие к центру сертификации и соответствие сертификата имени хоста.Для обычной проверки повторяю запрос без него:
Код:
curl.exe -v --max-time 15 https://example.org/
3. Проверяю HTTP на 80 порту
Код:
curl.exe -v --max-time 15 http://example.org/ -o NUL
Получаю:
Код:
HTTP/1.1 301 Moved Permanently
Location: https://example.org/
То есть frontend принимает соединение на TCP/80 и возвращает HTTP-редирект.
4. Проверяю конфигурацию frontend
Смотрю активную конфигурацию nginx:
Код:
nginx -T 2>&1 | grep -n -A40 -B5 'server_name.*example.org'
Frontend принимает HTTPS-запрос клиента, завершает TLS и проксирует запрос дальше на middle proxy по HTTP:
Код:
198.51.100.10
Пример:
Код:
location / {
proxy_pass http://198.51.100.10;
```
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 https;
```
}
5. Смотрю error.log на frontend
В логах повторяется:
Код:
upstream timed out (110: Connection timed out)
while reading response header from upstream
upstream: "http://198.51.100.10:80/"
Ключевая часть:
Код:
while reading response header from upstream
Это не
TCP connect timeout.Frontend дошёл до стадии чтения заголовка ответа от upstream, но не получил данных от него в течение интервала
proxy_read_timeout.Сам
proxy_read_timeout задаёт интервал между операциями чтения от upstream, а не максимальное время получения всего ответа.Значит, следующий шаг — проверить middle proxy напрямую.
6. Проверяю middle proxy с frontend
На Linux:
Код:
curl -v --connect-timeout 5 --max-time 10
http://198.51.100.10/
-H 'Host: example.org'
Получаю:
Код:
Connected to 198.51.100.10 port 80
GET / HTTP/1.1
Host: example.org
а затем:
Код:
Operation timed out after 10000 milliseconds
with 0 bytes received
TCP-соединение с middle proxy устанавливается. curl отправляет HTTP-запрос, но ответ не приходит в течение заданных 10 секунд.
7. Проверяю nginx локально на middle proxy
Подключаюсь к middle proxy и выполняю:
Код:
curl -v --connect-timeout 5 --max-time 10
http://127.0.0.1/
-H 'Host: example.org'
Результат тот же:
Код:
Connected to 127.0.0.1 port 80
GET / HTTP/1.1
Host: example.org
Operation timed out after 10000 milliseconds
with 0 bytes received
Проблема воспроизводится даже через localhost: соединение устанавливается, но HTTP-ответ не приходит в течение заданных 10 секунд.
Следовательно, внешний участок:
Код:
пользователь -> frontend -> middle proxy
не является причиной самого зависания на middle proxy.
Сам этот тест ещё не показывает, на каком внутреннем этапе зависает nginx. Это становится понятно из его логов.
8. Проверяю состояние middle proxy
Код:
uptime
free -m
df -h
systemctl status nginx
nginx -t
Явных признаков перегрузки нет, nginx запущен, а
nginx -t проходит успешно:
Код:
Active: active (running)
syntax is ok
test is successful
То, что конфигурация nginx не менялась, ещё ничего не гарантирует: могли измениться маршрутизация, firewall, ACL или фильтрация на стороне сети или провайдера.
9. Нахожу следующий upstream
Для быстрого поиска:
Код:
nginx -T 2>&1 | grep -nE -A5 -B2
'upstream|proxy_pass|server_name.*example'
Если блок большой, просматриваю полный вывод:
Код:
nginx -T
Следующими узлами оказались:
Код:
192.0.2.10
192.0.2.20
10. Нахожу ключевую ошибку на middle proxy
В
error.log middle proxy появляется уже другая ошибка:
Код:
upstream timed out
while connecting to upstream
Например:
Код:
upstream:
"http://192.0.2.20:80/announce.php"
Именно:
Код:
while connecting to upstream
означает, что nginx не смог установить соединение с upstream в пределах
proxy_connect_timeout.В отличие от:
Код:
while reading response header from upstream
здесь проблема уже находится на стадии установления TCP-соединения.
11. Проверяю origin напрямую
С middle proxy:
Код:
curl -v --connect-timeout 5 --max-time 10
http://192.0.2.10/
-H 'Host: example.org'
Получаю:
Код:
Trying 192.0.2.10:80...
connect to 192.0.2.10 port 80 failed:
Connection timed out
Проверяю второй origin:
Код:
curl -v --connect-timeout 5 --max-time 10
http://192.0.2.20/announce.php
-H 'Host: tracker.example.org'
Результат такой же:
Код:
connect to 192.0.2.20 port 80 failed:
Connection timed out
Здесь curl уже не доходит до:
Код:
Connected to ...
TCP-соединение с origin не устанавливается в течение заданных 5 секунд.
Это основной результат всей диагностики.
PHP и веб-приложение не могут быть непосредственной причиной именно этого
TCP connect timeout, потому что соединение ещё не дошло до HTTP-обработки.12. Проверяю маршрут
На middle proxy:
Код:
ip route get 192.0.2.10
ip route get 192.0.2.20
Например:
Код:
192.0.2.10 via 198.51.100.1 dev eth0 src 198.51.100.10
ip route get показывает результат route lookup ядра: выбранный next-hop и интерфейс.Это не доказывает, что пакеты действительно проходят весь путь до origin.
13. Проверяю firewall
На middle proxy проверяю, не блокируется ли локально исходящий или возвращающийся трафик.
iptables:
Код:
iptables -L OUTPUT -n -v --line-numbers
iptables -L INPUT -n -v --line-numbers
iptables -S
nftables:
Код:
nft list ruleset
UFW:
Код:
ufw status verbose
На origin проверяю входящие правила и наличие слушающего порта:
Код:
ss -lnt '( sport = :80 )'
При необходимости:
Код:
ss -lntp '( sport = :80 )'
и соответствующий firewall:
Код:
iptables -L INPUT -n -v --line-numbers
iptables -S
или:
Код:
nft list ruleset
Также учитываю внешние ACL, security groups и firewall провайдера, если они используются.
14. Дополнительно проверяю ICMP и другие TCP-порты
Код:
ping -c 4 192.0.2.10
ping -c 4 192.0.2.20
В моём случае:
Код:
100% packet loss
Сам по себе
ping ничего не доказывает, потому что ICMP может фильтроваться.При необходимости проверяю несколько TCP-портов:
Код:
nc -vz -w 5 192.0.2.10 22
nc -vz -w 5 192.0.2.10 80
nc -vz -w 5 192.0.2.10 443
Это помогает понять, ограничена ли проблема одним сервисом или затрагивает направление шире.
15. При необходимости смотрю tcpdump
Если нужно понять, приходит ли TCP SYN на origin:
Код:
tcpdump -ni any tcp port 80
После определения фактического source IP фильтр можно сузить:
Код:
tcpdump -ni any host 198.51.100.10 and tcp port 80
Если SYN при корректной точке захвата и с учётом возможного NAT/SNAT на origin не виден, он не доходит до наблюдаемого сетевого стека origin.
Для данного инцидента этого уровня проверки было достаточно.
16. Проверяю с другой точки сети
С другого сервера:
Код:
curl -v --connect-timeout 5 --max-time 10
http://192.0.2.10/
-H 'Host: example.org'
Если:
Код:
другой сервер -> origin = OK
а:
Код:
middle proxy -> origin = connect timeout
значит, проблема связана с middle proxy или сетевым путём от него до origin.
Именно такой подход — проверять доступность с каждого сервера по очереди — в моём случае быстро показал проблемный участок.
Установленная причина
Непосредственно сетевой диагностикой было установлено следующее.
Frontend фиксировал:
Код:
while reading response header from upstream
Middle proxy фиксировал:
Код:
while connecting to upstream
Прямой запрос с middle proxy до origin показывал:
Код:
Connection timed out
Следовательно, подтверждённый симптом при соединении:
Код:
middle proxy -> origin
был:
Код:
TCP connect timeout
Эти тесты сами по себе не позволяют определить ТСПУ как конкретный механизм фильтрации.
После того как проблемное направление было локализовано, я провёл дополнительную проверку. По её результатам причиной сбоя на этом сетевом направлении была определена фильтрация с использованием ТСПУ.
Речь идёт именно о конкретном инциденте и конкретном сетевом направлении.
Почему увеличение timeout не решило бы проблему
Можно увеличить:
Код:
proxy_connect_timeout
proxy_read_timeout
Но если TCP-соединение с origin не устанавливается из-за сетевой проблемы или фильтрации, увеличение timeout только заставит nginx и пользователя дольше ждать тот же результат.
Перезапуск nginx тоже ничего не исправит, если nginx нормально работает и проблема находится дальше по сетевой цепочке.
Мой короткий алгоритм диагностики
- Проверяю DNS.
- Проверяю HTTP и HTTPS через
curl. - Смотрю, на каком этапе останавливается запрос.
- Проверяю
error.lognginx. - Смотрю, какой upstream используется.
- Проверяю этот upstream напрямую.
- Повторяю проверку на следующем сервере.
- При
while connecting to upstreamпроверяю прямое TCP-соединение до origin. - Проверяю
ip route getи firewall. - При необходимости использую
ping,ncиtcpdump. - Сравниваю доступность origin с другой точки сети.
Главное — проверять цепочку последовательно, переход за переходом, а не начинать с изменения timeout и перезапуска сервисов.
Вывод
Финальная картина выглядела так:
Код:
Пользователь
|
v
Frontend
203.0.113.10
|
| соединение с middle работает
v
Middle proxy
198.51.100.10
|
| TCP connect timeout
X
|
v
Origin
192.0.2.10 / 192.0.2.20
На frontend я видел:
Код:
while reading response header from upstream
а на middle proxy:
Код:
while connecting to upstream
Прямой
curl с middle proxy до обоих origin подтверждал:
Код:
Connection timed out
Именно последовательная проверка:
Код:
клиент
->
frontend
->
middle proxy
->
origin
позволила без лишних изменений конфигурации быстро найти проблемный участок.
После дополнительной проверки была установлена и конкретная причина сбоя на этом направлении — фильтрация с использованием ТСПУ.
Главный вывод для меня простой: если nginx сообщает
upstream timed out, сначала нужно понять, на каком именно переходе перестаёт ходить трафик, а уже потом менять конфигурацию или искать проблему в приложении.