Network debugging toolkit
ping and mtr for reachability, dig for names, curl -v and openssl s_client for HTTP and TLS, ss for sockets, and tcpdump for when nothing else explains it.
Layer by layer
# 1. is the name resolving?
dig +short api.example.com
# 2. is the host reachable, and is there loss?
ping -c 4 api.example.com
mtr -rwzc 20 api.example.com
# 3. is the port open and accepting?
nc -vz api.example.com 443
curl -sS -o /dev/null -w '%{http_code} %{time_total}s\n' https://api.example.com/health
# 4. is the protocol conversation correct?
curl -v https://api.example.com/health 2>&1 | head -40| Layer | Tool | Question it answers |
|---|---|---|
| Name resolution | dig, getent hosts | Does the name resolve, to the right address? |
| Reachability | ping, mtr | Is there a path, and is it losing packets? |
| Port state | nc -vz, ss -tlnp | Is something listening and accepting? |
| HTTP behaviour | curl -v | Status, headers, redirects, timing |
| TLS | openssl s_client | Chain, protocol, ALPN, expiry |
| Sockets | ss, lsof -i | Which process holds which connection |
| Packets | tcpdump | What actually went over the wire |
Reading curl timings
curl -sS -o /dev/null -w '
dns %{time_namelookup}s
connect %{time_connect}s
tls %{time_appconnect}s
ttfb %{time_starttransfer}s
total %{time_total}s
http %{http_code}
size %{size_download} bytes
' https://api.example.com/health- A large
time_namelookupmeans DNS is slow, not the server. - A large gap between
time_connectandtime_appconnectpoints at the TLS handshake. - A large gap from
time_appconnecttotime_starttransferis server processing — the request is on the wire, waiting for the app. - A high total with a low TTFB means the transfer is slow, so look at payload size and bandwidth.
- Repeat with
--resolveto bypass DNS and compare, which isolates the resolver quickly.
# pin the address to test one backend behind a load balancer
curl -v --resolve api.example.com:443:203.0.113.10 https://api.example.com/health
# show only the handshake and headers
curl -sS -v -o /dev/null https://api.example.com/health 2>&1 | grep -E '^[<>*]'When nothing above explains it
# capture on a specific interface and port, without resolving names
tcpdump -i eth0 -nn -s 0 port 443 -c 50 -w /tmp/cap.pcap
# show TCP flags, which reveals retransmits and resets
tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-syn|tcp-rst) != 0'
# a connection refused arrives as a reset, not silence
# silence usually means a firewall dropped the packet| Observation | Likely meaning |
|---|---|
| SYN sent, no reply | A firewall is dropping silently |
| SYN sent, RST returned | The port is closed; nothing is listening |
| SYN, SYN+ACK, then RST from client | The client rejected the handshake; check for an L7 proxy |
| Repeated retransmissions of the same segment | Packet loss or an MTU problem |
| Connection resets after a period of idleness | An idle timeout in a NAT or firewall |
| TLS alert immediately after the handshake | Certificate or protocol mismatch |
⚠️
Capture on the smallest scope you can. A packet capture contains credentials, tokens and personal data, and a full-interface capture on a busy host fills a disk in minutes. Filter by port and host, cap the packet count, and delete the capture when you are done.
FAQ
Why does curl work but my application cannot connect?
Usually a proxy setting.
curl may be honouring HTTPS_PROXY while the application is not, or the other way round. Compare environments before changing any code.Why is ping blocked but the service reachable?
ICMP is frequently filtered while TCP is permitted. Reachability should be tested with a TCP connect, not with ping.
Related
TCP in depth: handshake, windows and congestion control Proxies, load balancers and CDNs
Last refreshed 2026-09-18.