error_page 对 SSL 握手失败无效,因其发生在 TLS 层,早于 HTTP 处理流程;仅当 TLS 握手成功但证书校验失败(如 495/496)时 error_page 才生效。

Nginx 的 error_page 指令本身无法直接捕获或处理 SSL 握手失败(如证书过期、域名不匹配、协议不支持、SNI 缺失等),因为这类错误发生在 TLS 层,远早于 HTTP 请求被解析和转发的阶段——此时 Nginx 甚至还没收到一个合法的 HTTP 请求行,error_page 根本不会触发。
为什么 error_page 对 SSL 握手失败无效
SSL/TLS 握手是 TCP 连接建立后的加密协商过程。一旦失败(例如客户端发送了不被信任的 SNI、使用已废弃的 TLS 1.0、或证书链校验失败),Nginx 通常会直接关闭连接,返回一个 TLS alert(如 handshake_failure 或 bad_certificate),而不会生成任何 HTTP 状态码(如 495/496),更不会进入 HTTP 处理流程。因此:
-
error_page 495 /ssl-error.html这类配置在握手失败时完全不生效; - 浏览器看到的是“NET::ERR_CERT_INVALID”“ERR_SSL_VERSION_OR_CIPHER_MISMATCH”等本地提示,而非你定义的页面;
- 所有基于
error_page+return/proxy_pass的 HTTP 层错误页方案,对此类问题无能为力。
真正可行的缓解策略
虽然无法“拦截并重定向”握手失败,但可通过以下方式提升终端用户感知和运维响应效率:
-
启用并正确配置 495/496 错误页(针对已通过 TLS 但证书校验失败的请求):当 client 与 server 完成 TLS 握手,但 Nginx 启用了
ssl_verify_client on或使用了ssl_client_certificate,且客户端证书无效/缺失时,Nginx 会返回 HTTP 495(SSL Certificate Error)或 496(SSL Certificate Required)。这时error_page 495 /cert-error.html是有效的,可自定义提示页; -
前置 TLS 健康检查与主动告警:用外部工具(如
openssl s_client、ssllabs-scan或 Prometheus +blackbox_exporter)定期检测证书有效期、链完整性、协议支持情况,并在异常时通知运维,从源头避免问题发生; -
提供清晰的诊断辅助信息(非页面,而是文档+工具):在公司内网或运维 Wiki 中维护一份《SSL 故障自查清单》,包含常见报错含义、对应 OpenSSL 检测命令(如
openssl s_client -connect example.com:443 -servername example.com)、以及如何查看 Nginx error log 中的 TLS 相关日志(需开启error_log ... debug;并筛选ssl_关键字); -
对老旧客户端做协议/密码套件兼容性兜底(谨慎使用):若业务必须支持旧设备(如某些嵌入式系统),可在
ssl_protocols和ssl_ciphers中适度放宽限制(例如保留 TLS 1.1,加入DES-CBC3-SHA),但需权衡安全风险,且这属于预防而非事后处理。
一个实用的 495/496 自定义页面示例(仅适用于证书校验失败场景)
确认你启用了客户端证书验证后,可添加如下配置:
server {
listen 443 ssl;
server_name example.com;
<pre class="brush:php;toolbar:false;">ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_client_certificate /path/to/ca-bundle.crt;
ssl_verify_client on;
# 此处生效:仅当 TLS 握手成功,但客户端证书不合法时
error_page 495 496 /ssl-error.html;
location = /ssl-error.html {
root /usr/share/nginx/html;
internal;
}}
然后在 /usr/share/nginx/html/ssl-error.html 中放置友好的提示,例如:
证书验证未通过
请确认您使用的客户端证书是否有效、未过期,且由我们信任的证书颁发机构签发。
如有疑问,请联系技术支持:support@example.com
不复杂但容易忽略:SSL 握手失败不是 HTTP 错误,它发生在更底层。想“优雅提示”,重点不在 Nginx 页面配置,而在预防、监控与用户沟通路径的设计。











