Проверка TCP соединения: curl, nc, nmap и фильтрация порта
Как проверить TCP соединение с внешнего узла: curl, nc, nmap, tcping, mtr и PowerShell. Что значит filtered, чем DROP отличается от REJECT.
Сайт не открывается. Или приложение перестало отвечать клиентам. Что делаем? Большинство по рефлексу стучит ping. Толку с этого, если честно, мало. Пинг работает по ICMP, а приложение слушает TCP на конкретном порту. Это разные вещи на разных уровнях, и одно про другое ничего не говорит. ICMP может проходить, пока сервис на 443-м лежит. И наоборот тоже бывает: пинг режется, а сервис живой.
Поэтому я давно не пингую первым делом. Я лезу в TCP — пытаюсь поднять реальное соединение на тот порт, который интересует. Прошло — значит SYN дошёл, сервер ответил SYN/ACK, я ответил ACK, рукопожатие случилось. Не прошло — а вот тут начинается разбор, кто конкретно мешает: то ли порт закрыт, то ли по дороге сидит фаервол.

Дальше — по инструментам. Локальные ss/netstat пропускаю, статья про взгляд снаружи.
Почему ICMP врёт чаще, чем кажется
Пинг живёт лет тридцать и сидит у всех в мышечной памяти. Но за эти тридцать лет в интернете много чего поменялось:
- Корпоративные фаерволы давно режут ICMP пачками — он не нужен для работы, а флудить через него удобно.
- Облачные Security Groups (AWS, GCP, Azure) работают на default-deny: пока ты явно не добавил правило для Echo Request — его не будет.
- Host-фаерволы (iptables, nftables) тоже его часто прихлопывают по дефолту security policy.
Получается забавная картина: сервер живой, приложение работает, клиенты ходят — а ping тебе пишет 100% packet loss. И ты, увидев это, идёшь будить дежурного, хотя никакого инцидента нет.
Обратная история бывает не реже. ICMP проходит — потому что роутер у провайдера его пропускает — но на 5432 (Postgres) или 4222 (NATS) трафика нет. Пинг говорит «ок», а БД лежит.
Реальная проверка TCP-соединения — про L4, а не про L3. Пинг про L3, и он про другое.
curl с верботным флагом
HTTP/HTTPS я не пингую вообще. curl с -v рассказывает всё, что нужно:
curl -v https://example.com
Если порт нестандартный — двоеточие после хоста:
curl -v http://192.168.1.50:8080
Самое полезное тут не сама страница, а лог соединения. Курл проговаривает каждый шаг: куда полез, что ответили, где встал.
* Connecting to ...— висит — TCP вообще не поднялся, проблема в сети или в фаерволе по дороге.* Connected to ...— но дальше тишина — порт открыт, но L7 молчит, ищи бэкенд.HTTP/1.1 502или504— сеть жива, лёг апстрим.- Сломался на TLS — порт открыт, проблема в сертификате или в протоколе (часто старый клиент пытается в TLS 1.0).
Из опыта этого хватает, чтобы понять, к кому идти — сетевикам, разработчикам или самому себе.
netcat: быстрая проверка хендшейка
Когда приложение по своему TCP-протоколу — БД, очередь, gRPC, что угодно — curl не годится. Беру nc:
nc -zv 8.8.8.8 53
Голый nc не подходит — он встанет и будет ждать. Поэтому два флага:
-z— только проверить хендшейк и сразу выйти,-v— чтобы он голосом, а не молча.
На выходе либо Connection to 8.8.8.8 53 port [tcp/domain] succeeded!, либо refuse/таймаут.
Диапазон через дефис:
nc -zv 192.168.1.10 22-80
Удобно, когда забыл, на каком порту тестовая админка, и не хочется лезть в README.
tcping: для тех, кто хочет наблюдать
Иногда нужен не точечный ответ, а график во времени. Деплой только что прошёл — хочется убедиться, что доступность не плавает. Или клиент пишет «иногда не работает», и надо это поймать. Для таких случаев — tcping. По сути это пинг, только по TCP: раз в секунду поднимает короткое соединение и пишет, сколько занял хендшейк.
На Ubuntu ставится так:
sudo apt install tcptraceroute
Запуск:
tcping example.com 443
В выводе обычные строчки вида port 443 open. time=14.2 ms, бегущие сверху вниз. Когда среди них вылезает closed или таймаут — вот и пойман момент.
Сам по себе tcping отвечает только на одно: открыт ли порт прямо сейчас. Где именно проблема, он не покажет. Для этого следующий пункт.
mtr — когда коннект флапает и непонятно где
Если tcping показал, что соединение нестабильное, я переключаюсь на mtr. Это гибрид трассировки и пинга: он сыплет пробами на каждый хоп маршрута и считает потери. Сразу видно, на каком участке деградация.
По умолчанию mtr бьёт ICMP, но я почти всегда форсирую TCP-пробы — потому что ICMP, как мы уже выяснили, врёт:
sudo mtr --tcp -P 443 example.com
Пример того, что прилетает обратно:
HOST: laptop Loss% Snt Last Avg
1.|-- 192.168.1.1 0.0% 100 0.4 0.5
2.|-- 10.0.0.1 0.0% 100 1.2 1.5
3.|-- isp-edge.example.net 0.0% 100 3.1 3.4
4.|-- transit-1.upstream.net 38.0% 100 8.4 12.7
5.|-- transit-2.upstream.net 0.0% 100 9.1 9.3
6.|-- example.com 0.0% 100 10.2 10.4
Когда на каком-то промежуточном хопе вылезает 30–40% потерь, а дальше снова 0% — это обычно не реальный лосс, а rate-limit ICMP на роутере (даже когда мы льём TCP, ответы-то по ICMP идут). Внимание стоит обращать на потери, которые держатся до конца маршрута. Вот они уже реальные.
С таким выводом можно идти к сетевикам или к хостеру и предметно говорить, на каком участке деградация. Без mtr разговор обычно заканчивается фразой «у нас всё работает».
nmap, когда надо понять «почему»
nc отвечает: «доступен или нет». nmap отвечает: «если нет — то что именно происходит». Это уже разведка, не быстрая проверка.
nmap -p 443 8.8.8.8
Три возможных статуса:
- open — порт слушает.
- closed — хост ответил RST, но процесса нет.
- filtered — самый интересный, разбираем ниже.
Что такое filtered: DROP против REJECT
filtered означает, что nmap отправил SYN и не получил внятного ответа. Не SYN/ACK, не RST, не ICMP-ошибки — ничего. Пакет ушёл в пустоту. Это работа фаервола, который сидит где-то по дороге. Может быть на хосте (iptables/nftables), может быть в облачной SG, может быть на корпоративном Cisco ASA, может на edge-роутере у провайдера.
Блокирует он двумя способами, и снаружи они выглядят по-разному:
- DROP — пакет молча выкидывается, как будто его и не было. Никаких ответов. С твоей стороны коннект тупо висит до системного таймаута, а это 60–120 секунд в дефолте линукса. Nmap пишет
filtered. - REJECT — фаервол отстреливается. Либо ICMP-пакетом «Destination Unreachable: Administratively Prohibited», либо TCP с флагом RST. С твоей стороны — мгновенный
Connection refused. Nmap пишет либоclosed(если прилетел RST), либоfilteredс пометкой про ICMP-блокировку.
Мнемоника: висит — это DROP, refused — REJECT. По одному только этому симптому я в большинстве случаев уже знаю, кто режет: хост настроен на DROP, или edge с REJECT-политикой.
История с проды
Деплой недавно. Caddy в контейнере работает, локально с сервера curl -v localhost:443 отвечает корректно, всё ок. С моей машины из дома — мгновенный refused. Первая мысль очевидная: я что-то накосячил в nftables на сервере. Лезу проверять. Правила на месте, INPUT принимает 443.
Делаю nmap -p 443 server-ip с домашнего ноута. Статус — filtered. То есть не закрытый порт, и не моя локальная свистоперделка. Что-то по дороге.
Запускаю nmap с третьей машины — это уже коллега помог из другой подсетки и другого провайдера. Тоже filtered. Значит проблема не на моей стороне и не на моём провайдере. Скорее всего косяк у провайдера хостинга.
Залез в личный кабинет. У них на upstream-роутере default-DROP политика для всего, кроме whitelist. 80, 22, 8080 разрешены, 443 — нет. Запросил открытие, через минут двадцать поехало.
Если бы я не глянул nmap'ом с двух точек, я бы час сидел в логах Caddy и journalctl. А там пусто, потому что коннект до сервера вообще не доходил.
PowerShell: Test-NetConnection
В винде я обычно использую WSL, но возможно кто-то ещё пользуется PowerShell:
Test-NetConnection example.com -Port 443
С подробностями:
Test-NetConnection example.com -Port 443 -InformationLevel Detailed
В выводе TcpTestSucceeded: True/False плюс латентность. Аналог nc -zv без сторонних утилит.
Если терминала под рукой нет
Бывает, что проверить порт нужно с внешней точки, а не со своей машины. Например, у клиента вылезла проблема, и ты не уверен, не его ли это локальный фаервол. Или нужно показать заказчику, что снаружи всё открыто.
Для таких случаев у нас на PingMap есть инструмент проверки TCP-порта — он бьёт с наших узлов в разных регионах и показывает open / closed / filtered плюс время хендшейка. Один URL, одна кнопка.
Рядом по теме: проверка ICMP и trace, HTTP-статус-чек, распределённая HTTP-проверка.
Итог
Если запомнить только три вещи — пусть это будут эти:
- Пинг — не про TCP. ICMP может проходить, пока сервис лежит, и наоборот. Не используй его как первый сигнал.
- Симптом «висит» vs «refused» уже половина диагностики. Висит — DROP, refused — REJECT. Это сразу подсказывает, где смотреть: на хосте или на роутере по дороге.
- Если
nmapпишетfiltered— проверь с другой точки. Чужой провайдер, мобильный интернет, чужая сетка — нужно подтвердить, что блокирует не «по дороге к тебе», а реально перед сервером.
Шпаргалку выше сохрани в закладки — пригодится в следующий пожар.
FAQ
Пинг же показывает, что сервер живой — зачем ещё что-то?
Пинг — это ICMP, он про L3. Приложение слушает TCP на конкретном порту, это L4. Пинг может проходить, пока сервис лежит, и наоборот. Реальная проверка — это попытка поднять TCP-соединение на нужный порт.
А что значит filtered у nmap?
Это когда nmap отправил SYN и ничего не получил в ответ — ни SYN/ACK, ни RST, ни ICMP-ошибки. Фаервол по дороге дропает пакет молча.
DROP и REJECT — в чём разница?
DROP пакет молча выкидывает, и ты ждёшь системного таймаута. REJECT активно отказывает — TCP RST или ICMP «Destination Unreachable», и ты сразу видишь refused.
UDP можно теми же тулами проверять?
Частично. nc -zuv — попытка, но UDP без подтверждения, поэтому «успех» чаще означает «пакет ушёл и никто не возразил». nmap -sU точнее, но в разы медленнее TCP-сканирования.
Telnet говорит refused, но сервер же работает?
Скорее всего, на сервере действительно нет процесса, слушающего этот порт. Хост получил SYN и ответил RST — это и есть refused. Поведение по умолчанию для пустого порта.
Связанные инструменты
Читать ещё
Настройте мониторинг за 2 минуты
Добавьте URL, выберите канал уведомлений — и получайте сигнал о реальных проблемах.