curl -I检查HTTPS返回405:HEAD、GET与证书验收分层排查

curl -I检查HTTPS地址,返回405 Method Not Allowed;浏览器打开同一路径却正常。先别重签证书:-I发的是HEAD,不是"把GET正文藏起来"。下面用隔离对照拆开TLS身份、HTTP方法与健康检查。

一、405先查方法,不要先拆证书配置

RFC 9110将405定义为:服务器知道这个方法,但目标资源不支持它。源站生成405时必须带Allow,列出当前支持的方法;它不是证书错误码,也不等于服务完全不可用。

HTTPS直连中,在验证未关闭且收到真实HTTP响应的前提下,405发生在TLS之后。但单看一行405不能证明整个站点、回源链路或所有节点证书正确;代理CONNECT失败等还要按实际连接层区分。

二、先固定版本与请求条件

本次实测Linux、curl 7.61.1及OpenSSL 1.1.1k后端,回环TLS 1.2、HTTP/1.1。旧版本仅作行为对照,不是部署建议;其他后端和版本应重跑负例。系统openssl命令与curl链接库也未必相同。

bash 复制代码
curl -V
openssl version

以下每个Shell块保存成独立Bash脚本运行,别把带exit的整段粘进登录Shell。example.com/health替换为有权测试的目标;本轮改为随机回环端口并显式指定测试CA,没有修改生产信任库。

-q放首位跳过默认curl配置,但不清环境;--noproxy只适用于允许直连的诊断。生产私有CA用授权--cacert材料,不能用-k把身份验证拆掉。诊断保存脱敏日志,勿外传Cookie和认证头。

三、-I是真的发送HEAD

bash 复制代码
curl -q --noproxy '*' --http1.1 -sS -I \
  --connect-timeout 5 --max-time 15 \
  -w '\nhttp=%{http_code} verify=%{ssl_verify_result}\n' \
  https://example.com/health
rc=$?
printf 'curl_exit=%s\n' "$rc"
exit "$rc"

这一段故意不加-f,分别观察HTTP码和进程退出码。本例HEAD收到405,curl仍退出0;加-f后为22。退出0说明这次传输完成,不等于HTTP状态符合业务约定。

HEAD响应不得发送内容,通常应返回相应GET的头字段,但规范允许省略只有生成正文时才能确定的字段。它不是"每个头必须完全相同"的保证,更不是GET业务执行成功证明。

配图由DM-Fox辅助生成,经技术与可读性复核;四格为本例检查维度,不是无条件成功流程。

四、保持地址不变,用GET做对照

bash 复制代码
curl -q --noproxy '*' --http1.1 -sS \
  --connect-timeout 5 --max-time 15 \
  -D get.headers -o get.body \
  -w 'http=%{http_code} verify=%{ssl_verify_result}\n' \
  https://example.com/health
rc=$?
printf 'curl_exit=%s\n' "$rc"
exit "$rc"

这里去掉-I,让curl按默认GET请求;-D只保存响应头,-o保存正文,都不会把GET变成HEAD。用可写测试目录,避免覆盖已有get.headers和get.body;这些文件存在也不能替代退出码检查。

本轮对照 结果 服务端事件
可信CA,HEAD HTTP405,退出0 收到HEAD
可信CA,GET HTTP200,退出0 收到GET
HEAD加-f HTTP405,退出22 收到HEAD
HEAD换无关CA 退出60,HTTP000 没有HTTP请求

同一测试证书和路径,仅方法不同就能得到405与200。错误CA负例在HTTP之前停止;000是curl未拿到响应码的占位,不是服务器发出的状态。实验服务模拟方法限制,不代表所有Web框架都这样处理。

五、不要用-X HEAD冒充-I

curl官方明确:-X只改请求方法字符串,不同步改变内部处理方式。正确HEAD应使用-I或--head,而不是仅写-X HEAD。

对照服务允许HEAD并返回200、Content-Length: 3,但不发正文;-I退出0,-X HEAD在本机正常TLS关闭后报18、还差3字节。这里只描述本构建结果,不能保证所有关闭方式都报18。

HEAD带Content-Length并非必然错误:规范允许它表示对应GET本会发送的内容长度,不是让HEAD也传这么多正文。查"下载不完整"之前,先看自己有没有把请求方法和客户端预期配成两套。

六、把健康检查写成明确契约

若需求是检查GET业务,就用GET,并同时限定状态和内容。以下最小契约只接受200及精确三个字节OK加换行;用于可信、可写的测试目录。生产应按真实接口定义校验,不把所有接口都改成此格式。

bash 复制代码
set -u
umask 077
work=$(mktemp -d ./health.XXXXXX) || exit 1
trap 'rm -rf -- "$work"' EXIT
if code=$(curl -q --noproxy '*' --http1.1 -fsS \
  --connect-timeout 5 --max-time 15 \
  -o "$work/body" -w '%{http_code}' \
  https://example.com/health); then
  :
else
  rc=$?
  printf 'curl失败:%s\n' "$rc" >&2
  exit "$rc"
fi
[ "$code" = 200 ] || exit 1
printf 'OK\n' > "$work/expected" || exit 1
cmp -s "$work/body" "$work/expected" || exit 1
printf 'GET健康契约通过\n'

-f拒绝本例405等HTTP错误,但不替代精确200判断,也不验正文。代码不跟随重定向;302、空200、错误内容及错误CA均须失败。最终HTML四个代码块已提取执行,临时文件由trap清理;强制杀进程或掉电仍需另管残留。

七、如何定位是谁拒绝了HEAD

现象 下一步
HEAD405,GET200 核对路由方法、网关及WAF策略
两种方法均证书错误 回到CA链、名称与时间检查
HEAD200,GET业务失败 检查正文和依赖,不凭HEAD放行
入口与源站表现不同 固定终点与逻辑名称逐层对照

日志要绑定同一时刻、路径和方法;Server头或错误页面风格只是线索,不能独立证明响应来自哪层。受控测试源站仍要保持正确SNI和验证名称,不用IP URL随便加Host代替证书验收。

  • 确认监控到底要求HEAD可用,还是GET契约通过。
  • 若契约要求HEAD,修复路由或网关支持后重测,不能偷换成GET掩盖缺陷。
  • 若契约只要求GET,调整监控方法需明确记录,而非关闭TLS验证。
  • 保留正确CA与错误CA、正确内容与错误内容两组负对照。

八、参考资料与边界

HEAD像询问"这份资料有什么属性",GET才是实际取资料;两次问法不同,不能把405直接归到证书头上。本稿仅完成回环TLS与HTTP方法实验,未验收生产代理、WAF或HTTP/2、HTTP/3。

相关推荐
我命由我123451 小时前
Python 开发 - Python 虚拟环境
运维·开发语言·后端·python·http·https·java-ee
一直C1 小时前
嵌入式 Linux 驱动开发学习笔记:内核定时器、platform 总线与 I²C 驱动
linux·驱动开发·嵌入式硬件·学习·imx6ull
承渊政道2 小时前
Linux网络学习【传输层协议UDP】
linux·网络·网络协议·学习·ubuntu·udp
雯宝2 小时前
usb_serial_init/tty_driver/alloc_tty_driver/tty_register_driver/usb_register
linux
澜山奕秋2 小时前
Rhcsa 二
linux·运维·服务器
小此方2 小时前
Linux网络(三十三):告别O(n)噩梦:从select/poll的挣扎到epoll红黑树+回调的降维打击,一文打通IO多路复用
linux·网络·驱动开发
2301_777998342 小时前
Linux 网络:数据链路层
linux·服务器·网络
迷途之人不知返2 小时前
【文件系统】-1-磁盘与扇区的定位方法
linux
无足鸟ICT2 小时前
【RHCA+】yum源配置(软件仓库)
linux·运维