Коротко. Сайт может не открываться из России, даже когда сервер стоит в Москве у российского хостера и работает исправно. Причина бывает в диапазоне IP-адресов: провайдеры фильтруют нетипичные блоки целиком. Ниже разбор случая и методика диагностики.
Симптомы, которые не сходились
Переехал на свой сервер, а утром сайт не открывался ни из дома, ни с телефона. При этом сервер отвечал на ping идеально, ноль потерь. Через VPN сайт открывался, но через раз. Без VPN не открывался вообще.
Последний пункт и сбивал с толку: сеть до сервера есть, пакеты ходят, а страница не грузится.
Час я потратил впустую
Гадал вместо того, чтобы мерить. Первая версия была про VPN: внешний адрес показывал Франкфурт, трафик до Москвы шёл через Германию, задержка 100 мс. Версия красивая и неверная, потому что без VPN сайт не открывался вообще, а этого она не объясняла.
Вывод, до которого я дошёл поздно: если у гипотезы остаётся симптом, который она не объясняет, гипотеза неверна. Не «почти», не «частично». Неверна.
Как надо было: мерить серией
Одиночная проверка бесполезна, когда отказ плавающий. Двадцать пять запросов подряд через VPN дали 18 успешных из 25, то есть 28 процентов запросов не проходило.
Дальше нужна контрольная группа, без неё замер не значит ничего. Проверил другие хосты через тот же канал: google.com ответил 15 раз из 15, сайт самого хостера 12 из 12, мой сервер 18 из 25. Канал живой, чужие сайты открываются, не открывается конкретный адрес.
Странность, которая всё запутала
Разложил проверку по уровням и получил картину, противоречащую сама себе. Ping дал ноль потерь на тридцати пакетах. Чистый TCP-коннект на 443 и на 22 проходил 20 раз из 20, SSH-сессия работала. А обычный HTTP-запрос падал в трети случаев.
В момент отказа curl показывал таймаут при нулевом времени соединения. Ноль здесь значит, что соединение не установилось вообще: запрос ушёл, ответа нет. Не разрыв на середине передачи, а молчание.
Шесть версий, которые не подтвердились
Авария на ноде хостера. В панели действительно висело уведомление об аппаратном сбое. Отпала, когда зашёл на сервер: нагрузка нулевая, память свободна, nginx без перезапусков, контейнеры работают десять часов. Сбоило бы железо, страдал бы и SSH.
Nginx или Docker не принимают соединения. Разумно, если бы падал только веб. Но чистый TCP-коннект на 443 давал 20 из 20, а при переполнении очереди он бы тоже падал.
MTU и фрагментация. Path MTU до сервера оказался 1300 вместо 1500, типичная подпись туннеля. Отпала на контрольном замере: до старого хостинга MTU был точно такой же, и там всё работало.
Сертификат. Отпала сразу: нулевое время соединения значит, что до TLS дело не доходит, а сертификат читается уже после установки соединения.
Отсутствие IPv6-записи. У моей машины IPv6 нет вообще, все замеры шли по IPv4, форсированная проверка давала те же потери.
IP в чёрных списках. Проверил шесть баз, включая Spamhaus и SpamCop. Все чистые.
Что дало ответ
Дамп трафика на сервере. Вот это надо было делать первым: запускаешь запись пакетов на сервере и одновременно бьёшь запросами снаружи.
Картина оказалась однозначной. На двадцать пять запросов пришло двадцать пять входящих соединений, ни одно не потерялось по пути туда. Сервер отвечал на все за одну десятитысячную секунды. А на моей стороне в этот момент был таймаут. Терялся ответ, обратный путь.
Развязка
Оставалось проверить очевидное, до чего я дошёл последним: как ведёт себя сервер с реального канала, мимо VPN. Ping прошёл шесть раз из шести за 16 мс без потерь. TCP на 443 не прошёл ни разу из десяти. Другие сайты с того же интерфейса работали.
Это фильтрация. Не потери и не перегрузка, а избирательная блокировка TCP к конкретному адресу при живом ICMP.
Почему именно мой адрес
Проверил, кому принадлежит блок адресов. Оказалось, адрес российского хостера, физически стоящий в Москве, формально принадлежит латиноамериканскому диапазону, зарегистрированному в LACNIC.
Хостеры выкупают такие блоки легально: свободных адресов в европейском реестре давно нет, а IPv4 нужны. Юридически всё чисто. Но магистральные провайдеры фильтруют по спискам, и нетипичные для России диапазоны попадают под них целиком. Хватит того, что сосед по подсети когда-то дал повод.
Попросил у хостера другой адрес. Выдали из соседнего семейства, проверил: Бразилия. Снова LACNIC, те же грабли.
Чем кончилось
Переехал к другому хостеру. Перед оплатой проверил их диапазоны: европейский реестр, зарегистрированы на Россию. Потом проверил доступность их сетей со своего канала, пять из пяти.
Результат до и после: было 0 успешных запросов из 10 при задержке 100 мс, стало 12 из 12 при 25 мс. Перенос занял часа три. Всё было в Docker, так что механика простая: архивы с проектами, файлы с ключами, сертификат, статика сайта, DNS.
Методика, если симптомы похожие
Мерьте серией, а не одиночным запросом. Плавающий отказ на одиночной проверке выглядит как случайность.
Берите контрольную группу. Другие хосты через тот же канал. Без этого замер ничего не доказывает.
Разложите по уровням: ping, чистый TCP, TLS, HTTP. Место, где ломается, сужает круг втрое. Ping не идёт, значит маршрут или хост лежит. Ping идёт, TCP нет, это фильтрация. TCP идёт, TLS рвётся, фильтрация по SNI. Всё идёт, падает только HTTP, вот тогда смотрите свой сервер.
Проверьте обходной путь. Есть VPN, сравните с ним и без него. Именно эта мелочь скрывала у меня проблему сутки.
Снимайте дамп трафика на сервере. Он отвечает на главный вопрос: доходят ли запросы и отвечает ли сервер. Остальное догадки.
Проверьте, чей диапазон. Увидели LACNIC, AFRINIC или APNIC у российского хостера, причина скорее всего найдена. Просите адрес из европейского реестра.
Что вынес
Гипотеза, которая не объясняет все симптомы, неверна. Я трижды строил версию, объясняющую часть картины, и трижды она разваливалась о факт, который я решил считать неважным.
Дамп с сервера стоит часа гаданий. Полдня я перебирал версии снаружи, хотя доступ к серверу работал с самого начала.
Хостер видит только свою сеть. Первая линия честно пропинговала сервер изнутри стойки, получила 0,68 мс и написала, что всё работает. Формально они правы. Сдвинулось, только когда я прислал таблицу: столько успешных запросов, столько таймаутов, вот трассировка, вот дамп. С цифрами не поспоришь.
Спрашивайте про диапазон до оплаты. Одна строчка в чат поддержки, из какого блока выдаётся адрес, экономит день переезда.
Если нужна помощь
Диагностика чужой инфраструктуры, перенос сайта на другой сервер, настройка деплоя — это настройка сервера, а дальше обычно нужна поддержка сайта: следить, чтобы такое не повторилось, и чинить, когда всё-таки случится. Проверить свой сайт на технические проблемы, ошибки и соответствие 152-ФЗ можно прямо сейчас: бесплатное демо проверки, работает на живых данных, без регистрации.