How do you use curl to debug a web or API problem from a Linux box?
Show the answer
Say this first
curl -v shows each stage: DNS result, TCP connect, TLS handshake, the request headers sent and the
response headers received, so I can see where it breaks. -w with timing variables splits the delay into
DNS, connect, TLS and server time. --resolve sends the request to a specific backend IP while keeping
the real hostname for SNI and the Host header. The exit code names the failing stage: 6 DNS, 7 connect,
28 timeout, 35 TLS handshake, 60 certificate.
Go deeper
Verbose run (trimmed)
$ curl -sv -o /dev/null https://app.example.com/health
* Trying 203.0.113.80:443...
* Connected to app.example.com (203.0.113.80) port 443
* ALPN: server accepted h2
* Server certificate:
* subject: CN=app.example.com
> GET /health HTTP/2
> Host: app.example.com
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/2 200
< content-type: application/jsonLines with * are curl's own notes, > is what it sent, < is what came back.
Where the time goes
$ curl -s -o /dev/null -w 'dns %{time_namelookup} connect %{time_connect} tls %{time_appconnect} ttfb %{time_starttransfer} total %{time_total}\n' https://app.example.com/health
dns 0.004213 connect 0.031870 tls 0.089402 ttfb 0.912337 total 0.915006The values are cumulative from the start. Here DNS, TCP and TLS are fast and the server takes about 0.8 s to produce the first byte: an application problem, not a network one.
Other options I reach for
--resolve app.example.com:443:10.0.20.15: test one backend behind a load balancer with correct SNI.-Iheaders only,-Lfollow redirects,-kskip certificate checks (testing only).--connect-timeout 3and-m 10so a script does not hang.-4or-6to force a family,-x http://proxy:3128to test through a proxy.curl -v telnet://10.0.20.15:5432as a quick TCP port test when telnet and nc are not installed.
They will ask next
Why use --resolve instead of putting the IP in the URL with a Host header?
With HTTPS the client sends the hostname in SNI and checks the certificate against the URL host. An IP in the URL breaks both, so you test a different thing than real users.
--resolvechanges only where the TCP connection goes.curl to an HTTPS site hangs right after the TCP connect. What do you suspect?
The handshake starts but large packets (the server certificate) do not arrive: a path MTU black hole, or a middlebox dropping TLS. Compare with a lower MTU or
ss -tnito see retransmissions.
Traps
- Leaving
-kin production scripts and hiding real certificate problems. - Reading
time_totalalone. The breakdown is what tells you who to call.