Bollywood HungamaMohit Suri buys Mercedes-Benz EQS worth over Rs 1.60 crore, adds luxury EV to his garagePunchUS tightens green card rules with stricter public charge testESPNTransfer rumors, news: Al Hilal eye shock move for KaneDaily MaverickKyrgios provisionally suspended after testing positive for cocaineRTP DesportoMédio Rodrigo Mora deixa FC Porto para reforçar Romaוואלהתשתית מוכנה ושלטי חוצות: עופר וינטר בדרך להקמת מפלגת ימין חדשהThe Jerusalem Post'You're a clown': Ben-Gvir faces verbal assault from residents during visit to northern IsraelInquirer EntertainmentBTS’ ‘Swim’ nominated for Song of the Year at MTV VMAs01netGTA 6 : un énorme leak de gameplay circule avant la présentation Netflix du 27 aoûtInquirerHouse panel to assess whether to present Madriaga in Duterte trialCBS NewsIran's top diplomat and Trump cast doubt on any hope for a peace dealNumeramaCette grosse promotion sur la Switch 2 tombe juste avant la hausse de septembre
The Daily Newsstand · Free, Always
Wednesday, August 19, 2026

Что делать, если сервер доступен по SSH, а сайт не открывается

Translate

Мониторинг шлёт тревогу, пользователи пишут, что «всё лежит», но SSH пускает на сервер. Значит, машина включена, и 22-й порт доступен. О домене, портах 80 и 443, TLS, веб-сервере и приложении это пока ничего не говорит… Кажется, что нужно просто перезапустить nginx, но перезапуск стирает часть следов и может превратить частичную аварию в полную беду. Под катом расскажу, как пройти путь запроса сверху вниз и найти место, где он остановился.

Шаг 1. Воспроизводим ошибку

Фраза «сайт не открывается» ещё ни о чём не говорит, сначала проверьте его из другой сети. Если проблема только у вас, возможны локальный DNS-кеш, запись в /etc/hosts, блокировка адреса или маршрут провайдера. Если жалуются все, запускайте curl на клиентской машине:

curl -v --connect-timeout 5 --max-time 15 https://example.com/

Также посмотрите на последнюю успешно пройденную стадию:

  • Could not resolve host ведёт к DNS. Если в выводе нет Connected to, то соединение не дошло до готового TCP или другой стадии подключения. 

  • Когда TCP уже установлен, а дальше висит TLS-рукопожатие, проверять нужно сертификаты и настройки TLS. 

  • Если HTTP-запрос ушёл, но ответа нет до --max-time, ищите проблему с прокси, приложением и его зависимостями.

Мгновенный Connection refused чаще всего значит, что на порту никто не слушает, либо фильтр ответил отказом. Тайм-аут чаще указывает на DROP, проблему маршрута или зависание на поздней стадии. Однако одной строки с ошибкой мало, смотрите весь вывод.

Коды HTTP тоже могут сузить поиск. При 502 шлюз получил некорректный ответ от upstream, при 504 — не дождался ответа вовремя, а 503 означает временную недоступность или перегрузку и может прийти как от прокси, так и от приложения.

Отдельно сравните IPv4 и IPv6:

curl -4 -v --connect-timeout 5 --max-time 15 https://example.com/

curl -6 -v --connect-timeout 5 --max-time 15 https://example.com/

Если IPv4 работает, а IPv6 нет, проверьте AAAA-запись, маршрутизацию и слушающий сокет. Сломанную запись лучше исправить сразу, а не надеяться на то, что клиенты переключатся на другой протокол. 

Шаг 2. Сверьте DNS и нужный сервер

Зачастую SSH идёт на новый сервер по IP, а домен остаётся на старом адресе, или A-запись исправили, AAAA забыли, а некоторые резолверы ещё держат ответ в пределах TTL. Для проверки проблемы используйте: 

dig +short A example.com

dig +short AAAA example.com

В целом, сравните адреса с реальной схемой. За NAT публичного IP внутри виртуалки может не быть, а домен за CDN не должен указывать на origin. Нужный сервер проверяйте без правки DNS (некоторые команды буду писать с слэшами-переносами, чтобы в кучку всё не сваливалось):

curl -v \

  --connect-timeout 5 \

  --max-time 15 \

  --resolve example.com:443:203.0.113.10 \

  https://example.com/

--resolve подменяет IP, сохраняя доменное имя в HTTP и SNI. Если обычный запрос падает, а origin отвечает, смотрите DNS, CDN или балансировщик. Сервер, который принимает трафик только с адресов CDN, должен отклонить прямой запрос — его проверяют из разрешённого источника, либо через CDN.

Шаг 3. Узнайте, кто слушает 80 и 443

Дальше заходите по SSH и смотрите сокеты в текущем сетевом пространстве:

sudo ss -ltnp 'sport = :80'

sudo ss -ltnp 'sport = :443'

Пустой вывод означает, что в текущем network namespace нужный порт никто не слушает. Если строки есть, посмотрите, к какому адресу привязан сокет: 127.0.0.1:443 доступен только с самого сервера, а 0.0.0.0:443 принимает IPv4-соединения на всех его интерфейсах. Запись [::]:443 относится к IPv6 — может ли такой сокет одновременно принимать IPv4-соединения, зависит от параметра IPV6_V6ONLY и настроек приложения. Быстрее всего это проверить отдельными запросами (писал их выше). Для nginx команды такие:

sudo systemctl status nginx --no-pager -l

sudo nginx -t

sudo journalctl -u nginx --since '-30 minutes' --no-pager

Active: active (running) подтверждает, что процесс жив, но это не говорит о правильности виртуального хоста и upstream. А nginx -t проверяет синтаксис и доступность указанных в конфигурации файлов. Также после теста перечитайте конфиг без остановки старых воркеров:

sudo systemctl reload nginx

Для Apache используйте sudo apachectl configtest — юнит обычно называется apache2 в Debian-подобных системах и httpd в RHEL-подобных. Знаю, что вы всегда проверяете конфиг перед перезапуском, но на всякий случай решил напомнить. 

Шаг 3. Разделите аварию

Локальный запрос проверяет веб-сервер без внешнего маршрута. Для HTTP передайте правильный Host:

curl -v --noproxy '*' --max-time 15 http://127.0.0.1/ -H 'Host: example.com'

Для HTTPS нужен ещё и SNI:

curl -v --noproxy '*' --max-time 15 --resolve example.com:443:127.0.0.1 https://example.com/

Если сертификат уже известен как проблемный, запрос можно один раз повторить с -k. В мониторинг и рабочие скрипты этот ключ переносить не рекомендую.

Локальный 200 или ожидаемый редирект подтверждает, что веб-сервер обработал запрос с нужными Host и SNI через loopback. Если сайт по-прежнему недоступен, то проблему нужно искать между клиентом и сервером — в локальном файрволе, сетевом экране хостера, балансировщике, CDN или маршрутизации.

Локально работает, снаружи тишина

Если ситуация такая, то начните с правил на самом сервере. Сначала разберитесь, какой firewall backend используется. Для nftables команда будет такой:

sudo nft list ruleset

Если хост использует iptables, одного iptables -S мало, ведь он показывает только filter table текущего семейства. Для полной картины пригодятся:

sudo iptables-save

sudo ip6tables-save

К слову, Docker создаёт правила для bridge-сетей и публикации портов, а опубликованный порт способен обойти обычную логику ufw. Поэтому для начала уточните сетевой режим и firewall backend Docker. 

Следом глядите на сетевой экран в панели хостера — он находится за пределами гостевой ОС, поэтому в локальном наборе не появится. То же относится к балансировщику и списку разрешённых адресов на origin.

Если на сервере установлен Fail2ban, проверьте, не заблокировал ли он IP-адрес, с которого выполняется внешний запрос. В Fail2ban правила сгруппированы по jail, то есть по отдельным наборам фильтров для SSH, nginx и других сервисов. Первая команда покажет активные jail:

sudo fail2ban-client status

Затем подставьте имя нужного jail во вторую команду. В её выводе будет список заблокированных адресов:

sudo fail2ban-client status ИМЯ_JAIL

Если правила выглядят нормально, смотрите пакеты:

sudo tcpdump -ni any 'tcp port 80 or tcp port 443'

В другом окне повторите внешний curl. Если нет SYN на origin, значит, запрос потерялся раньше или ушёл на другой IP. Если SYN есть, но SYN-ACK не уходит, проверяйте firewall, policy routing и сокет. Если SYN-ACK ушёл, но клиент его не видит, смотрите обратный маршрут и провайдера.

Фронтенд отвечает, приложение нет

Если nginx принимает запрос, но возвращает 502 или 504, то проблема чаще всего находится между ним и приложением. То же касается ситуации, когда статическая страница открывается, а динамические разделы не работают. Тут смотрите на активную конфигурацию через sudo nginx -T и найдите директиву, по которой nginx передаёт запросы дальше: proxy_pass, fastcgi_pass или uwsgi_pass.

После этого обратитесь к upstream напрямую. Используйте тот же протокол, адрес, порт, Host и путь, которые указаны в конфигурации nginx:

sudo ss -ltnp 'sport = :8000'

curl -v --max-time 10 http://127.0.0.1:8000/health -H 'Host: example.com'

Порт 8000 и путь /health привёл для примера. Подставьте адрес и URL из конфигурации nginx. Если отдельного healthcheck нет, запросите рабочий динамический маршрут.

HTTP-upstream может быть подключён и через Unix-сокет. В таком случае смотрите через: 

curl --unix-socket /run/myapp/app.sock http://localhost/health

Сокеты из fastcgi_pass и uwsgi_pass работают не по HTTP, поэтому обычным curl их не проверить. Сначала проверьте наличие сокета, права на него, статус PHP-FPM и журналы. Для полноценного запроса потребуется FastCGI-клиент, например, cgi-fcgi, с параметрами конкретного приложения.

Для контейнеров нужны другие команды:

docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'

docker logs --since 30m --tail 200 myapp

docker inspect --format '{{json .State.Health}}' myapp

docker port myapp

Пустой docker port норма, если прокси находится в той же сети или используется host network. Up означает лишь, что жив PID 1, а Health появится только при настроенном healthcheck.

Базу проверяйте по тому же адресу и порту, которые использует приложение:

pg_isready -h 127.0.0.1 -p 5432 -d appdb

mysqladmin --host=127.0.0.1 --port=3306 ping

Напомню, имена юнитов зависят от пакета: postgresql.service бывает мета-юнитом, а MySQL может называться mysql. При 504 смотрите также блокировки, медленные запросы и пул соединений.

Шаг 4. Проверьте TLS 

Для OpenSSL 1.1.1+/3.x цепочку и соответствие имени можно проверить так:

openssl s_client \

  -connect 203.0.113.10:443 \

  -servername example.com \

  -verify_hostname example.com \

  -verify_return_error \

  -brief </dev/null

-servername передаёт SNI, -verify_hostname проверяет имя, а -verify_return_error не даёт s_client продолжить работу после ошибки проверки. А все сертификаты, присланные сервером, выводит команда:

openssl s_client \

  -connect 203.0.113.10:443 \

  -servername example.com \

  -showcerts </dev/null

На старой системе сначала смотрите openssl version (часть ключей может отсутствовать). Сертификат выбранного IP легче всего проверить через curl --resolve.

Шаг 5. Обратите внимание на ресурсы  

Если процессы на месте, но запросы висят, смотрите ресурсы:

df -h

df -i

free -h

uptime

vmstat 1 5

sudo journalctl -k -g 'oom|out of memory|killed process'

Первая строка vmstat 1 содержит средние значения с момента загрузки, текущую картину дают последующие. В systemd версий до 237 нет journalctl -g, там используйте:

sudo journalctl -k --no-pager \

  | grep -Ei 'oom|out of memory|killed process'

Если df -h показывает заполненную файловую систему, выясните, какой каталог занял место. Например, содержимое /var можно проверить так:

sudo du -xhd1 /var 2>/dev/null | sort -h

Ключ -x не даёт du переходить на другие файловые системы, а -d1 ограничивает проверку каталогами первого уровня. После уже можете изучить самый крупный каталог.

К слову, показания df и du иногда не совпадают — это происходит, когда файл уже удОлён из каталога, но процесс продолжает держать его открытым. Имени у файла больше нет, поэтому du его не видит, однако занятые блоки всё ещё учитываются в df. Найти такие файлы поможет команда:

sudo lsof +L1

Место освободится, когда процесс закроет файл, но в слепую его не завершайте. 

На счёт df -i. Если закончились inode, свободное место на диске не поможет — система не сможет создать новый файл. Причиной могут быть каталоги с множеством мелких файлов, например, кэшем, сессиями или очередью.

Если с диском всё в порядке, переходите к памяти и I/O. В выводе free -h смотрите прежде всего на available, а в vmstat следите за si и so. Постоянный обмен данными со свопом при низком available говорит о нехватке памяти. Записи Killed process в журнале ядра подтверждают, что до процессов уже добрался OOM Killer (про него рассказывал тут).

Высокий load при свободном CPU чаще всего связан не с вычислениями, а с процессами в состоянии D, которые ждут диск, NFS или другой I/O. На виртуалке проверяйте и %st в top — высокие цифры значат, что гипервизор регулярно забирает процессорное время у гостевой системы.

Есть ли короткий маршрут до причины

Начните с внешнего curl и определите, на какой стадии обрывается запрос. Затем проверьте DNS через dig и нужный origin через curl --resolve. На сервере посмотрите слушающие сокеты, состояние nginx и конфигурацию через nginx -t.

После этого выполните локальный запрос. Если локально сайт работает, а снаружи нет, проверяйте файрвол, сетевой экран хостера, балансировщик, CDN и маршрутизацию. Если nginx возвращает 502 или 504, переходите к upstream, приложению, сокетам и базе. TLS, диск, память и I/O проверяйте по симптомам, которые уже показали curl, журналы и состояние сервисов.

После починки НАСТРОЙТЕ УЖЕ внешний мониторинг, ротацию логов и алерты на диск, inode, память и сервисы. Если используете Certbot, прогоните certbot renew --dry-run. Автозагрузку проверяйте по реальным именам юнитов:

systemctl is-enabled nginx

systemctl is-enabled myapp

systemctl is-enabled postgresql@16-main

Последний юнит я привёл в пример, имя кластера и версия будут вашими. И не закрывайте последнюю SSH-сессию, пока не проверите новое подключение и запасную консоль хостера. К упавшему сайту слишком легко добавить ещё одну аварию, но уже на порту 22.

Делитесь в комментариях, сталкивались ли вы с такой ситуацией? Что в итоге оказалось причиной?

View the original on Хабр

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.