<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Troubleshooting on Jinying Tech Blog</title>
		<link>https://chejinying.com/tech/tags/troubleshooting/</link>
		<description>Recent content in Troubleshooting on Jinying Tech Blog</description>
		<generator>Hugo</generator>
		<language>en-us</language>
		
		
		
		
			<lastBuildDate>Wed, 02 Sep 2026 21:55:51 +0800</lastBuildDate>
		
			<atom:link href="https://chejinying.com/tech/tags/troubleshooting/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>curl Troubleshooting Cheatsheet</title>
				<link>https://chejinying.com/tech/posts/shell/curl/</link>
				<pubDate>Wed, 02 Sep 2026 21:55:51 +0800</pubDate>
				<guid>https://chejinying.com/tech/posts/shell/curl/</guid>
				<description>&lt;p&gt;Examples use &lt;code&gt;example.com&lt;/code&gt; and documentation IPs; swap in your target.&lt;/p&gt;&#xA;&#xA;&lt;div class=&#34;table-scroll&#34; role=&#34;region&#34; aria-label=&#34;curl cheatsheet&#34; tabindex=&#34;0&#34;&gt;&#xA;&lt;table class=&#34;curl-cheatsheet&#34;&gt;&#xA;  &lt;thead&gt;&#xA;    &lt;tr&gt;&lt;th&gt;Symptom&lt;/th&gt;&lt;th&gt;Goal&lt;/th&gt;&lt;th&gt;Command&lt;/th&gt;&lt;th&gt;Key details&lt;/th&gt;&lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td class=&#34;symptom&#34; rowspan=&#34;5&#34;&gt;First look&lt;/td&gt;&#xA;      &lt;td&gt;Headers only&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -I https://example.com/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;HEAD request; some apps mishandle HEAD, so use &lt;code&gt;-sS -D - -o /dev/null&lt;/code&gt; to test the real GET&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;See the wire&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -v -o /dev/null https://example.com/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;*&lt;/code&gt; info, &lt;code&gt;&amp;gt;&lt;/code&gt; sent, &lt;code&gt;&amp;lt;&lt;/code&gt; received; output goes to stderr&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Status only&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -sS -o /dev/null -w &#39;%{response_code}\n&#39; https://example.com/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;-sS&lt;/code&gt; hides progress but keeps errors&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Readable JSON body&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -sS https://api.example.com/users/42 | jq .&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;-sS&lt;/code&gt; keeps the progress meter out of the pipe&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Follow redirects&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -L http://example.com/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Exit &lt;code&gt;47&lt;/code&gt; = too many redirects&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td class=&#34;symptom&#34; rowspan=&#34;3&#34;&gt;Connection&lt;/td&gt;&#xA;      &lt;td&gt;Is the port open?&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -v telnet://db.example.com:5432&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;&#34;Connected to&#34; = TCP is reachable; no nc or telnet needed. Exit &lt;code&gt;7&lt;/code&gt; = connect failed&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Which backend answered?&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -sS -o /dev/null -w &#39;%{remote_ip}:%{remote_port} %{response_code}\n&#39; https://example.com/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Shows where a load balancer, DNS round-robin, or VIP sent you&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Bound the request&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl --connect-timeout 3 --max-time 10 https://example.com/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;--connect-timeout&lt;/code&gt; limits connection setup; &lt;code&gt;--max-time&lt;/code&gt; the whole request. Exit &lt;code&gt;28&lt;/code&gt; = timeout&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td class=&#34;symptom&#34; rowspan=&#34;2&#34;&gt;Slow / flapping&lt;/td&gt;&#xA;      &lt;td&gt;Phase timing&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -sS -o /dev/null -w &#39;dns=%{time_namelookup} conn=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n&#39; https://example.com/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Cumulative times: high dns &amp;rarr; resolver; conn&amp;minus;dns &amp;rarr; network/firewall; tls&amp;minus;conn &amp;rarr; handshake; ttfb&amp;minus;tls &amp;rarr; server&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Watch over time&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;while true; do curl -sS -o /dev/null -w &#39;%{response_code} %{time_total}\n&#39; https://example.com/health; sleep 1; done&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Catches rollouts, intermittent 5xx, and failovers&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td class=&#34;symptom&#34; rowspan=&#34;4&#34;&gt;DNS / routing&lt;/td&gt;&#xA;      &lt;td&gt;Pin host to IP&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl --resolve &#39;example.com:443:203.0.113.10&#39; https://example.com/health&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Keeps SNI, cert check, and &lt;code&gt;Host&lt;/code&gt;; exit &lt;code&gt;6&lt;/code&gt; = DNS failed&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Send to another backend&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl --connect-to &#39;example.com:443:backend.internal:8443&#39; https://example.com/health&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Like &lt;code&gt;--resolve&lt;/code&gt;, but can also change the port&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Hit an IP with a Host header&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -H &#39;Host: example.com&#39; http://203.0.113.10/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Plain-HTTP virtual-host test; for HTTPS use &lt;code&gt;--resolve&lt;/code&gt; so SNI matches&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;IPv4 vs IPv6&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -4 https://example.com/&lt;/code&gt; / &lt;code&gt;curl -6 https://example.com/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Isolates a broken address family&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td class=&#34;symptom&#34; rowspan=&#34;5&#34;&gt;TLS&lt;/td&gt;&#xA;      &lt;td&gt;Cert subject / expiry&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -sv -o /dev/null https://example.com/ 2&amp;gt;&amp;amp;1 | grep -E &#39;subject:|issuer:|expire date:&#39;&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Exit &lt;code&gt;60&lt;/code&gt; = cert verification failed; exit &lt;code&gt;35&lt;/code&gt; = handshake failed&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Private CA&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl --cacert ./internal-ca.pem https://service.internal/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Verifies the server with this PEM CA bundle&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Mutual TLS&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl --cert ./client.crt --key ./client.key --cacert ./ca.pem https://mtls.example.com/health&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Sends a client certificate&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Exact TLS version&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl --tlsv1.2 --tls-max 1.2 https://example.com/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;--tlsv1.2&lt;/code&gt; alone means 1.2 &lt;strong&gt;or newer&lt;/strong&gt;&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Confirm a cert issue&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -k https://dev.example.com/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Diagnosis only, never the fix&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td class=&#34;symptom&#34;&gt;HTTP version&lt;/td&gt;&#xA;      &lt;td&gt;Compare&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl --http1.1 https://example.com/&lt;/code&gt; / &lt;code&gt;curl --http2 https://example.com/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Exit &lt;code&gt;16&lt;/code&gt; / &lt;code&gt;92&lt;/code&gt; = HTTP/2 error&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td class=&#34;symptom&#34; rowspan=&#34;3&#34;&gt;Proxy&lt;/td&gt;&#xA;      &lt;td&gt;Through a proxy&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -x http://proxy.example.com:8080 https://example.com/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Also reads &lt;code&gt;http_proxy&lt;/code&gt;, &lt;code&gt;HTTPS_PROXY&lt;/code&gt;, &lt;code&gt;NO_PROXY&lt;/code&gt;&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Bypass all proxies&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl --noproxy &#39;*&#39; https://example.com/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Overrides the env vars&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;SOCKS / SSH tunnel&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl --socks5-hostname 127.0.0.1:1080 https://internal.example.com/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;The proxy resolves the hostname, e.g. through &lt;code&gt;ssh -D 1080 jumphost&lt;/code&gt;&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td class=&#34;symptom&#34;&gt;Local / container&lt;/td&gt;&#xA;      &lt;td&gt;Unix socket&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl --unix-socket /var/run/docker.sock http://localhost/version&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;For Docker, containerd, and local daemons&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td class=&#34;symptom&#34; rowspan=&#34;9&#34;&gt;API request&lt;/td&gt;&#xA;      &lt;td&gt;Reproduce a browser request&lt;/td&gt;&#xA;      &lt;td&gt;DevTools &amp;rarr; Network &amp;rarr; right-click &amp;rarr; &lt;strong&gt;Copy as cURL&lt;/strong&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Exact headers, cookies, and body; strip secrets before sharing&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Any method, JSON body&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -X PATCH --json &#39;{&#34;name&#34;:&#34;Alice&#34;,&#34;active&#34;:true}&#39; https://api.example.com/users/42&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;-X&lt;/code&gt; sets the method name only (&lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt;, &amp;hellip;); without it a body means POST. &lt;code&gt;--json&lt;/code&gt; adds the body plus &lt;code&gt;Content-Type&lt;/code&gt;/&lt;code&gt;Accept&lt;/code&gt; headers (curl 7.82+). Use &lt;code&gt;-I&lt;/code&gt;, not &lt;code&gt;-X HEAD&lt;/code&gt;, which hangs&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Any method, form body&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -X PUT --data-urlencode &#39;name=Alice&#39; --data-urlencode &#39;note=hello &amp;amp; bye&#39; https://api.example.com/users/42&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;URL-encodes each field; &lt;code&gt;Content-Type: application/x-www-form-urlencoded&lt;/code&gt;&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;GET with query&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -G --data-urlencode &#39;q=distributed systems&#39; --data-urlencode &#39;page=2&#39; https://api.example.com/search&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;-G&lt;/code&gt; moves the data into the query string&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Delete&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -X DELETE https://api.example.com/users/42&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;No body needed&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Basic auth&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -u alice https://api.example.com/private&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Prompts for the password; exit &lt;code&gt;67&lt;/code&gt; = auth rejected&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Bearer token&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -H &#34;Authorization: Bearer $API_TOKEN&#34; https://api.example.com/private&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Token comes from an env var, not the command history&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;CORS preflight&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl -i -X OPTIONS -H &#39;Origin: https://app.example.com&#39; -H &#39;Access-Control-Request-Method: POST&#39; https://api.example.com/users&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Check the &lt;code&gt;Access-Control-Allow-*&lt;/code&gt; response headers&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Fail on 4xx/5xx&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl --fail-with-body https://example.com/missing&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;curl 7.76+. Without it, a 404 or 500 still exits &lt;code&gt;0&lt;/code&gt;; with it, exit &lt;code&gt;22&lt;/code&gt;&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td class=&#34;symptom&#34;&gt;Feature missing&lt;/td&gt;&#xA;      &lt;td&gt;Check build&lt;/td&gt;&#xA;      &lt;td&gt;&lt;code&gt;curl --version&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;td&gt;Lists protocols, TLS backend, and features&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;/div&gt;</description>
			</item>
	</channel>
</rss>
