网页能打开,curl -I 却返回 405 Method Not Allowed
浏览器能打开网页,curl -I 却返回 405 时,对比同一路径的 HEAD 与 GET;用 GET 丢弃正文并输出状态码,分别判断传输失败、HTTP 异常和登录跳转。
浏览器能打开同一个地址,监控脚本里的 curl -I 却拿到下面的响应:
HTTP/1.1 405 Method Not Allowed
Allow: GET
先确认监控实际发送的请求方法。大写 -I 是 --head,对 HTTP 地址发送的是 HEAD 请求;直接访问网页通常使用 GET。目标路径允许 GET,却拒绝 HEAD,就可能出现浏览器正常、脚本报 405 的差异。仅凭这个响应,不能判定整个服务已经宕机。
本文处理的是 HTTP/HTTPS 地址的 HEAD 与 GET 差异。若你的请求本来就是 POST、PUT 或其他方法,或者 405 出现在代理的 CONNECT 阶段,需要按对应请求继续定位,不能直接套用这里的 GET 探测。
用同一个地址对比 HEAD 和 GET
以下示例使用 Linux、macOS 或其他提供 /dev/null 的类 Unix 环境。把示例域名和路径换成实际要监控的地址,不要把登录页或会触发业务操作的接口随手当成健康检查入口。
先保留原来的 HEAD 请求,只加上错误提示和超时限制:
curl -sS --max-time 15 -I 'https://app.example.com/status'
然后去掉 -I,用 GET 获取响应头,并丢弃响应正文:
curl -sS --max-time 15 -D - -o /dev/null 'https://app.example.com/status'
这里的 -D - 把响应头写到标准输出;小写 -o /dev/null 把正文写入空设备。它们改变输出位置,没有把请求方法改成 HEAD。在没有其他方法选项或配置干预时,这条命令发出 GET。小写 -i 也能显示响应头,但会把头部和正文放在同一输出流里,不适合直接代替这个“只查看 GET 响应头”的写法。
如果 HEAD 返回 405、Allow 里只有 GET,而 GET 返回预期的 200,说明该路径能够响应 GET,而当前探测用了它不接受的方法。此时应让监控请求与接口约定一致。
如果 GET 也报 405,就不能把问题归结为 -I。查看实际请求路径和 Allow,再核对该接口规定的方法。Allow 描述当前目标资源允许的方法;它不表示整个站点的所有路径都只支持这一组方法。若响应没有这个头,也不要自行推断允许 GET。
两次测试应使用相同的地址、认证条件和网络路径。浏览器已有的登录状态不会因为地址相同就自动出现在 curl 中。若改为 GET 后得到 401、403 或登录跳转,应继续处理对应的认证或路由问题,不要把所有差异都算成 HEAD 不兼容。
监控只需要状态码时,不必使用 -I
去掉 -I 后,curl 默认会把网页正文打印出来。为了保持监控输出只有状态码,应把正文输出与状态码输出分开:
curl -sS --max-time 15 -o /dev/null -w '%{http_code}\n' 'https://app.example.com/status'
-w 在传输后输出指定信息,%{http_code} 是 HTTP 响应码,\n 让输出另起一行。-sS 隐藏进度条,同时保留失败时的错误提示,避免 DNS、连接或 TLS 错误被静默选项一起隐藏。
这条命令仍会获取 GET 的响应正文,只是丢弃了本地输出。它不会像 HEAD 那样节省正文传输。频繁探测大页面时,应选择已有的轻量健康接口,并确认该接口的预期响应;不要因为输出只有三个数字,就把探测频率设得过高。
Windows 原生 curl 可按其环境使用 -o nul。这里的脚本和 /dev/null 示例按类 Unix Shell 编写,不应整段直接复制到 Windows CMD 批处理文件。
同时检查 curl 退出码与 HTTP 状态码
curl 成功完成一次 HTTP 传输,并不等于服务器返回 200。默认情况下,收到 404、405 或 500,也可能以退出码 0 结束;仅检查命令是否成功,会漏掉业务层面的异常。
反过来,出现网络故障时,即使输出中留下了状态码,也必须先看 curl 是否完整完成传输。下面的脚本把两类失败分开,适用于约定必须返回 200 的探测地址:
#!/bin/sh
url='https://app.example.com/status'
if status=$(curl -sS --max-time 15 -o /dev/null -w '%{http_code}' "$url"); then
if [ "$status" = '200' ]; then
printf 'HTTP %s\n' "$status"
exit 0
fi
printf 'Unexpected HTTP status: %s\n' "$status" >&2
exit 1
else
curl_rc=$?
printf 'curl transfer failed: exit %s\n' "$curl_rc" >&2
exit 1
fi
先保存现有探测命令和告警条件,再替换为与接口约定匹配的检查。若接口正常时返回 204,就应按约定调整判断;不要把这段脚本里的 200 当作所有接口的健康标准。
--fail 能让许多 HTTP 错误响应产生非零退出码,但它不是“只接受 200”的判断,也不能代替对预期状态码的检查。上面的脚本没有依赖它,而是明确区分传输失败与非预期 HTTP 响应。
复核跳转,避免把登录页的 200 当作恢复
排查时先不要加 -L。若结果是 301、302 等跳转,查看响应中的 Location,确定是否为预期的新地址。自动跟随跳转可能让最后的登录页返回 200,从而掩盖探测地址本身的问题。
确实需要跟随跳转时,诊断阶段可以同时输出最终地址:
curl -sS --max-time 15 -L -o /dev/null -w '%{http_code} %{url_effective}\n' 'https://app.example.com/status'
这条命令增加了最终 URL,因此不能未经调整就交给只解析三位数字的监控程序。确认跳转目标属于预期服务,并且最终内容确实代表健康状态,再决定是否在正式探测中允许跳转。
也不要保留 -I 再叠加 -X GET 来修补探测。-X 主要替换请求中的方法字符串,并不会相应改变 curl 的所有传输行为;直接移除 HEAD 选项,再设置输出位置,含义更清楚。
修复后的确认与回退
用正式监控实际运行的账户和环境,检查修改后的命令能返回预期状态码,再观察告警是否按约定恢复。手动终端正常而定时任务仍报错时,核对任务中是否仍残留 -I、是否调用了另一份脚本,以及实际使用的 URL 是否一致。
若命令看似没有 HEAD 选项却仍发送 HEAD,可进一步检查包装脚本、别名和 curl 默认配置文件。诊断时把 -q 放在 curl 的第一个参数位置,可以跳过默认配置文件;不过这也会跳过其中的代理、证书等必要设置,应先检查配置,再决定是否使用这一对照方式。
本次调整只需要改变探测方法和输出处理,不需要放宽服务器权限或关闭 HTTPS 证书验证。若新增监控逻辑产生误报,恢复保存的探测配置,再根据实际退出码、状态码和跳转目标修正。只有接口确实要求支持 HEAD 时,才另行检查服务端路由;不能从一次旧环境的 405 推断某个框架或全部版本都不支持 HEAD。


