ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

curl 从入门到实战:HTTP 请求调试与自动化脚本指南

curl 从入门到实战:HTTP 请求调试与自动化脚本指南 说实话我见过太多人把 curl 用成了“只会 GET 一下网址”的工具。但在我日常排查接口、写部署脚本、调试第三方 API 的时候curl 几乎是出场率最高的命令没有之一。它是全平台默认自带的 HTTP 请求工具——macOS、Linux 自带Windows 10 1803 之后的系统也内置了 curl.exe它不需要安装任何依赖一条命令就能把请求的每个环节看得清清楚楚。这篇文章我会从零开始把 curl 发起 HTTP 请求的完整细节讲透GET/POST 怎么写、头部和 Cookie 怎么带、HTTPS 证书问题怎么破、常见的报错码背后是什么原因最后再给几个能直接抄的脚本级玩法。新手可以按顺序读熟手建议直接跳到第四节和第五节那里全是踩坑总结。1. 一条 curl 命令背后HTTP 请求的完整拆解1.1 当你敲下 curl 网址时实际发生了什么curl 的底层是 libcurl一个用 C 写的网络传输库。你每敲一条命令它背后至少经历了这么几步解析 URL、DNS 解析出 IP 和端口、TCP 三次握手建立连接、发送 HTTP 请求行和请求头、接收服务器响应、按需断开或复用连接。以curl https://www.bing.com为例默认情况下它发送的请求长这样GET / HTTP/1.1 Host: www.bing.com User-Agent: curl/8.x.x Accept: */*注意两个容易被忽略的点第一curl 默认的 User-Agent 就是curl/x.x.x很多网站和网关看到这个 UA 会直接拒绝或者返回不同的内容后面讲 403 的时候会再提到第二curl 默认并不跟随重定向请求头里也没有 Authorization 之类的鉴权信息这些都是“按需添加”的。理解了它默认做了什么你才能判断报错到底出在哪个环节——是先连不上还是连上了但请求头不对还是响应处理有问题。1.2 HTTP 请求的四要素方法、URL、头部、请求体把 HTTP 请求类比成一次寄快递。URL 是收货地址方法是你想干什么头部是快递面单上的备注栏请求体是包裹内容。方法MethodGET 表示取件查询POST 表示寄包裹PUT/PATCH 表示修改包裹内容DELETE 表示退件HEAD 只问你有没有包裹只返回响应头不返回体OPTIONS 问你能提供哪些服务。日常 90% 的场景就是 GET 和 POST。URL由协议http/https、主机名、端口、路径、查询参数五部分组成。https://api.example.com:8443/v1/users?page2这种 URL端口默认是 443路径是 /v1/users查询参数是 page2。头部HeadersHost 告诉服务器访问的是哪个域名Content-Type 告诉服务器请求体是什么格式Authorization 传令牌Cookie 维持会话状态。curl 里用 -H 一个个添加。请求体BodyGET 一般没有POST/PUT 才有。常见格式有 x-www-form-urlencoded 表单、JSON、multipart/form-data 文件上传。初学的时候最容易犯的错是把查询参数和请求体混为一谈。查询参数写在 URL 的?后面适合 GET请求体是另外一段数据适合 POST。两者可以同时存在但各自有各自的格式规范。1.3 常用选项速查表先混个脸熟后面每一节都会详细讲先给一个速查表建立整体印象选项作用典型示例-X指定请求方法curl -X DELETE http://...-d发送表单格式请求体curl -d namezhang ...-H添加请求头curl -H Content-Type: application/json-o / -O保存到文件 / 按原文件名保存curl -o a.txt http://...-L跟随重定向curl -L http://...-k跳过证书校验curl -k https://...-v输出详细过程curl -v http://...-i输出响应头curl -i http://...-I只请求头HEADcurl -I http://...-u带用户名密码Basic Authcurl -u admin:123 ...-b / -c发送 / 保存 Cookiecurl -b a1 ...-F文件上传curl -F filea.txt ...--connect-timeout连接超时秒数curl --connect-timeout 5 ...--max-time整个请求最大秒数curl --max-time 10 ...-w定制输出格式curl -w %{http_code} ...记住一个思路curl 的选项大多是小写字母表示动作、大写字母有进阶含义记不清就curl --help或man curl比查网页快得多。2. 高频场景实战GET、POST 与下载的正确姿势2.1 GET 请求的三个细节重定向、UA、Cookie先看最基本的 GET。curl http://example.com/api/v1/users?page1会把响应内容直接打印到终端。但实际调试中这三件事必须注意。第一重定向。很多网站会把 http 跳转到 https或者把 www 跳到不带 www 的域名。curl 默认不跟过去只会收到一个 301/302 响应头。加上-L才会自动跟着 Location 跳转。用 -L 的时候最好配合--max-redirs限制跳转次数防止出现 A 跳 B、B 跳 A 的死循环。第二User-Agent。有些接口会校验 UA不带或者带默认 UA 会被拒。用-A Mozilla/5.0 ...伪装成浏览器或者-H User-Agent: my-app/1.0声明自己是一个应用。第三Cookie。登录态的接口可以在浏览器里复制 Cookie用-b带过去curl -b sessionidabc123; tokenxyz https://api.example.com/user/info如果你有一组从登录接口拿到的 Cookie需要后续请求持续携带可以用-c把响应里的 Set-Cookie 写进文件再用-b 文件名读出来。这一招在调试需要登录态的接口时极其好用省得每次手动复制。2.2 POST 请求的三种数据格式别再用错了POST 最核心的问题是 Content-Type 和请求体格式匹配。用-d时curl 默认的 Content-Type 是application/x-www-form-urlencoded也就是表单格式curl -X POST http://localhost:8080/api/login \ -d usernameadmin \ -d password123456这样发的请求体是usernameadminpassword123456。注意-X POST在这里其实可以省略因为只要带-dcurl 会自动把方法变成 POST。这是很多人不知道的小细节但也因此踩坑如果 API 要求 GET 方法 带参数结果你用了-d就悄悄变成 POST 了。跟后端联调的时候JSON 格式最常见。这时必须手动指定 Content-Typecurl -X POST http://localhost:8080/api/order \ -H Content-Type: application/json \ -d {goodsId: 1001, count: 2, remark: 尽快发货}单引号包裹 JSON 是为了防止 shell 把双引号里的内容做变量展开和分词。如果 JSON 里又有单引号或者你想在脚本里拼接变量推荐用双引号并转义内部双引号或者把 JSON 写进文件用-d body.json读取。文件上传用-F它会自动生成 multipart/form-datacurl -X POST http://localhost:8080/api/upload \ -F file/path/to/photo.jpg \ -F categoryavatar-F 前面的字段名要和后端约定一致-F 时也可以添加其他文本字段。这里多说一句表单里如果有中文或特殊字符-d会导致乱码要用--data-urlencode name张三让 curl 自动做 URL 编码这比你自己手动编码靠谱得多。AJAX 请求设置编码格式也是同一个道理前后端约定好 charset数据才不会变成问号。2.3 下载文件与断点续传curl 当下载器用同样很顺手。-O按服务器文件名保存-o指定本地文件名curl -O https://mirrors.aliyun.com/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-1810.iso curl -o my.iso https://example.com/download/latest.iso下载到一半断了用-C -从断点续传curl -C - -O https://example.com/download/latest.iso-C -的意思是从已有文件的当前位置继续特别适合大文件。配合-#显示进度条或者不加任何参数让进度条自动输出。还有一个高频用法是下载安装脚本直接执行类似这样curl -fsSL https://ollama.com/install.sh | sh-f表示服务器返回 4xx/5xx 时让 curl 报错而不是硬着头皮输出错误页面-s是静默模式不显示进度-S让静默模式下仍显示错误信息-L跟随重定向。这个组合几乎是社区安装脚本的标准写法。但我要泼一盆冷水pipe to shell 是把远程脚本直接 pipe 给 sh 执行等于把你的机器完全信任给那个域名生产环境、公司电脑上建议先curl -O下载下来人工审一遍再执行至少也要确认域名是你信任的官方源。2.4 调试三板斧-v、-i、-w排查问题的时候我最常用的三个组合是-v、-i和-w。-v输出完整交互过程包括 DNS 解析结果、SSL 握手细节、发出的请求头、收到的响应头。第一次看可能觉得信息量大但每行都有意义比如Trying 93.184.216.34:443... Connected说明 TCP 已经通了SSL connection using TLSv1.3说明握手成功。哪个环节卡住看 -v 的输出位置就能定位。-i只在响应前加上响应头比 -v 精简适合想看状态码和关键响应头Set-Cookie、Content-Type的场景。-w可以定制输出格式用于脚本自动化统计curl -s -o /dev/null -w 状态码:%{http_code} 总耗时:%{time_total}s 连接耗时:%{time_connect}s\n \ https://api.example.com/health-o /dev/null丢弃响应体-w只输出关心的指标。这一招在批量检查多个接口健康状态时非常实用后面第五节还会用到。3. HTTPS 与证书校验SSL 报错的真实原因3.1 HTTP 和 HTTPS 的本质区别HTTP 是明文传输你发的请求、服务器返回的内容在网络上任何一跳都可能被看到甚至篡改。HTTPS 就是在 HTTP 外面套了一层 TLS 加密隧道先通过证书完成身份验证再协商出对称密钥之后所有数据都是密文。所以线上凡是涉及账号、支付、敏感数据的接口必须是 HTTPS。对 curl 来说区别就是默认行为不同访问 HTTPS 时curl 会主动校验服务器证书是否可信。这个过程叫证书链验证服务器把它的证书发给 curlcurl 拿着系统里预置的 CA 根证书去逐级验证确认这个证书确实是某个受信任 CA 签发的、域名匹配、且没有过期。3.2 curl: (60) SSL certificate problem 的典型场景报错curl: (60) SSL certificate problem: unable to get local issuer certificate是 HTTPS 调试里最常见的问题。翻译成人话就是curl 拿着服务器的证书往上找签发它的 CA一路找到根证书发现不在本机信任列表里。常见的原因有四种自签名证书。内网测试环境经常用 openssl 自己签证书不走公开 CA。curl 不认识自然报错。中间证书缺失。服务器只发了站点证书没把中间 CA 证书一起发过来导致验证链断裂。系统的 CA 证书库过期或损坏。Linux 上可以通过更新 ca-certificates 包解决。服务端证书域名不匹配。访问的域名和证书里的 CN/SAN 对不上属于服务端配置问题。对应的解决办法按优先级排序如果是自签名或内网证书拿到证书文件后用--cacert指定信任的证书curl --cacert /path/to/ca.crt https://internal.example.com如果是系统 CA 库问题更新 ca-certificatesmacOS 上如果是自建根证书需要手动加入钥匙串并信任。如果只是这个域名和证书不匹配那就是服务端配错了找运维修证书别在客户端硬绕。3.3 -k 是双刃剑别一刀切遇到证书报错就加-k是新手最容易犯的错。-k 等价于--insecure意思是跳过证书校验。加上它TLS 加密还在但“对方是不是真的服务器”这个验证被取消了相当于你打电话时完全不核实对方身份直接报银行卡号。什么场景可以用 -k本地开发的测试环境、纯内网的临时调试、确定网络环境可信且只是排查问题。什么场景绝对不要用生产环境、涉及真实用户数据和支付信息的请求、任何你不完全信任的网络。正确思路是把“跳过校验”当作临时手段最终要去解决证书信任问题而不是让 -k 成为常态。4. 高频报错与排查实录从 7 到 60 的进阶之路4.1 curl: (7) Failed to connect 到底卡在哪curl: (7) Failed to connect to 127.0.0.1 port 8080: Connection refused这类错误意思是 TCP 层就没连上。大多数人看到 “Connection refused” 第一反应是服务挂了但“连不上”其实分好几种情况Connection refused端口上没有任何程序在监听或者服务只监听了 localhost 而你在用局域网 IP 访问。先确认服务进程是否在跑ss -lntp | grep 8080。No route to host / Operation timed out地址不可达可能是防火墙丢包、网络不通、跨网段路由问题。本地 ping 一下、telnet 一下端口能快速区分。Could not resolve hostDNS 解析失败。nslookup 域名看解析是否正常或者检查系统的 DNS 配置。还有一类容易忽略对方服务正常但你的请求被防火墙拦截表现为长时间卡住最后 timeout。排查思路记住这个顺序先确认服务监听状态再确认网络能通最后确认防火墙放行。curl -v会把每个阶段卡在哪一步打得清清楚楚大多数时候根本不用猜。顺带说一个很常见的场景在浏览器里能访问在服务器上 curl 同一个地址却 Connection refused。多半是目标服务只监听了 127.0.0.1而服务器上 curl 访问的是外网 IP或者安全组/防火墙没放行。这时候先在本机curl http://127.0.0.1:端口试试如果本机通、外部不通问题基本就在监听地址和防火墙。4.2 curl: (22) 403 Forbidden被访问控制拒之门外curl: (22) The requested URL returned error: 403不是连接层的问题是 HTTP 层已经连上了但服务器决定不给你看。403 的常见原因和对应的破解思路默认 UA 被识别。网站检测到 curl/8.x 的 User-Agent 直接拒绝。用-A Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...伪装。缺少 Referer防盗链。下载图片、附件时常见服务器要求请求必须来自指定页面。用-e https://www.example.com/page补上。IP 被限制或需要登录态。检查是否需要-b带 Cookie或者-u带 Basic Auth。触发了 WAF 或风控。请求频率太高、请求特征太明显被当成爬虫。降低频率、带上完整的浏览器请求头UA、Accept、Accept-Language 都从 DevTools 里复制会改善。判断到底是哪一种用-v看响应头如果是 nginx 的 403错误日志里通常会有原因如果返回了验证码页面或风控提示就是被风控了。还有一个细节403 和 404 有时是故意混淆的服务器不想暴露资源是否存在你要是 curl 一个不存在的路径得到 404而真实路径 403说明是权限问题而不是路径问题。4.3 上游 502/400/500状态码语义与链路排查HTTP 状态码是服务器给你的“答复等级”。5xx 是服务器自己出问题4xx 是你请求有问题。实际联调里这条经验特别重要400 Bad Request请求体格式不对、JSON 语法错误、必填字段缺失。最常见的坑是 Content-Type 声明为 application/json但 body 实际不是合法 JSON或者字段类型不匹配。先检查你发的 body 是不是和接口文档完全一致。有 Java 背景的读者可能见过 Feign 报错[500] during [GET] to [http://...]那是调用方把上游 500 包装成了 FeignException真正要查的是被调服务。502 Bad Gateway网关后面的服务没起来、崩溃、或者响应超时。看到 502 不要先怀疑自己先去看上游服务的健康状况和日志。就像看到upstream_status: 400这种日志意思是网关把请求转给上游上游返回了 400问题在上游的请求处理。503 Service Unavailable服务过载或正在维护。504 Gateway Timeout上游响应太慢网关等不下去了。检查上游接口的慢查询和大事务。有个真实的案例Docker 拉镜像报错error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled ...本质是 Docker 守护进程访问官方镜像仓库的网络出问题了跟 curl 报错排查道理一样确认 DNS、确认网络连通性、必要时更换可用的镜像源。这类“上层工具连接远端服务失败”的错误用 curl 直接打一下那个 URL 就能复现和定位。4.4 包管理工具下载失败的实战conda HTTP 000conda 用户大概率都见过这类报错CondaHTTPError: HTTP 000 CONNECTION FAILED for url https://repo.anaconda.com/...HTTP 000 是 conda 自定义的一种表示“根本没拿到 HTTP 状态码”的错误本质是连接层失败了跟 curl: (7) 是一回事。排查步骤先用 curl 测试目标源是否能访问curl -I https://repo.anaconda.com/pkgs/main/win-64/repodata.json。如果 curl 也失败就是网络问题如果 curl 成功而 conda 失败继续往下查。确认 conda 配置的源没问题conda config --show channels。源响应慢或不同步时切换到其他镜像站通常能解决但要注意镜像的同步完整性和更新频率。检查系统里有没有残留的网络相关环境变量这类配置经常导致请求走错路径甚至连接失败需要按你所在单位的网络规范确认后再处理而不是自己随意改动。我遇到过最离奇的一次是 conda 因为系统时间是错的导致 TLS 证书校验失败报错也是 000。用date看时间差了好几年同步时间之后一切正常。所以 SSL 相关的问题里系统时间永远要查一遍。5. 从命令到自动化curl 进阶玩法与脚本技巧5.1 JSON 输出格式化再也不是一串天书接口返回的 JSON 在终端里经常是一行挤成一团的字符串人眼看着费劲更别提提取字段。最简单的格式化方式curl -s https://api.example.com/users | jq .jq 是处理 JSON 的命令行神器。没装 jq 时可以用 python 顶一下curl -s https://api.example.com/users | python -m json.tool在 iTerm2 这类终端里还可以把自己的命令别名加个函数比如把 curlj 定义为curl ... | jq后续调试 API 会舒服很多。提取字段直接jq .data.list[0].name做判断就jq -e 条件配合返回值判断接口是否满足预期能省掉一大段脚本逻辑。5.2 批量请求与并发--parallel 系列curl 7.66.0 之后支持了多请求并行非常适合批量探测场景。基本用法是把多个 URL 放在一个命令里curl --parallel --parallel-immediate \ https://api.a.com/health \ https://api.b.com/health \ https://api.c.com/health--parallel 开启并行--parallel-immediate 让所有请求立即发起而不是按顺序排队--parallel-max 3 限制并发数。实际用的时候建议配合 -w 把每个 URL 的状态码和耗时打出来一眼看出哪个服务拖后腿。注意输出顺序可能和请求顺序不一致如果必须对应用 -w 里带上%{url_effective}变量。这种并行方式写脚本非常香比如定期巡检几十个内部服务一条命令搞定比 for 循环里串行发请求快得多。要理解“并发”和“批量”的区别也很简单并发是同一时刻多个请求在跑批量是多个请求按顺序跑curl --parallel 就是让批量变成并发。5.3 连接复用与性能参数别小看 keep-aliveHTTP 1.1 默认支持 keep-alive同一个连接可以连续发多个请求省去重复的 TCP 握手和 TLS 协商。curl 的复用机制是在同一个命令行里指定多个 URL 时如果它们指向同一个主机curl 会自动复用连接。这就是“连接复用”在 curl 场景下的实际体现对 HTTPS 接口尤其重要因为一次 TLS 握手的开销远大于普通 TCP。脚本里批量请求同一主机的多个接口尽量合并成一条 curl 命令而不是起多个进程。如果要观察性能-w 里的这些时间指标要会看time_namelookupDNS 解析耗时time_connectTCP 连接耗时time_appconnectTLS 握手完成耗时time_starttransfer从开始到收到第一个字节time_total总耗时它们之间的差值能帮你定位瓶颈如果 time_appconnect 远远大于 time_connect握手慢考虑 TLS 会话复用如果 time_starttransfer 和 time_total 差距大服务端处理慢是后端问题。超时参数也要区分--connect-timeout 只管 TCP 连接建立的阶段--max-time 是整个请求从开始到结束的总时限。两者同时设置是标准做法防止连接卡死或者慢接口拖垮整个脚本。5.4 让浏览器帮你生成 curl 命令这个技巧真的救过我很多次。遇到一个只在浏览器里能复现的接口问题你不需要自己猜请求头、Cookie、签名参数打开浏览器 DevTools 的 Network 面板找到那条请求右键选择 Copy as cURL直接粘贴到终端就能复现curl https://api.example.com/xxx \ -H accept: application/json \ -H cookie: ... \ --data-raw ...这个命令把请求头、Cookie、请求体、请求顺序全部还原是排查“为什么浏览器能成功、代码里失败”的利器。复制出来之后再用 -v 或者去掉某些头做二分法就能定位是哪个头是必需的。如果你需要监控浏览器发出的所有请求做更细的分析可以在 DevTools 里查看也可以借助浏览器扩展把请求导出成 curl 或 HAR 格式做后续分析对于“怎么看某个页面到底请求了哪些接口”这种需求这几乎是最快路径。同理Swagger/Knife4j 这类接口文档工具生成的示例里通常也提供 curl 形式的请求示例后端的接口文档直接复制 curl 就能联调比自己拼参数省心太多。我自己习惯的做法是所有接口文档一律先生成 curl 示例再转成代码先保证命令通再翻译成业务代码排错范围会小很多。6. 高频问题速查表与 Windows 环境避坑现象可能原因解法curl: (7) Connection refused端口没监听/服务没起ss -lntp 确认监听检查服务curl: (7) Operation timed out防火墙拦截/网络不通ping、telnet 分段定位curl: (6) Could not resolve hostDNS 解析失败nslookup 检查改 DNScurl: (60) SSL certificate problem自签名/中间证书缺失/时间不对--cacert 指定证书检查系统时间curl: (22) 403UA/Referer/IP 被限制-A 伪装 UA、-e 补 Referer返回乱码编码格式不匹配确认接口 charset用 iconv 转码URL 带 只拿到一半参数shell 把 解释为后台执行整个 URL 加引号请求很慢DNS 慢/握手慢/后端慢-w 拆时间指标定位-d 带中文乱码未做 URL 编码用 --data-urlencode下载文件不完整中断-C - 断点续传127.0.0.1 拒绝连接服务没起或端口错误先 curl 127.0.0.1 确认本机状态再补充两个 Windows 环境的问题。一是 Windows 的 cmd 和 PowerShell 对引号和特殊字符的处理跟 Linux 不一样从网上复制的 curl 命令在 cmd 里经常因为单双引号问题报错建议统一在 PowerShell 里执行特殊情况用 PowerShell 自带的 Invoke-WebRequest别名 iwr替代。二是在老版本 Windows 上确实没有内置 curl要么装一个独立版本要么用 iwr别在 cmd 里死磕单引号。我自己用 curl 这么多年最大的体会是它把“网络问题”从玄学变成了可分解的环节DNS 一步、TCP 一步、TLS 一步、HTTP 一层每层都有对应的输出和报错。遇到问题不要慌先-v看卡在哪一步再对症下药。最后再分享一个小习惯我在脚本里固定加上--connect-timeout 5 --max-time 15以及-fsS三件套宁可让请求明确失败也绝不让脚本因为一个挂起的请求卡半个钟头。这个习惯帮我避开过很多次线上事故建议你也从一开始就养成。
返回列表