服务器和代理
no "ssl_certificate" is defined:Nginx 默认站点没配证书
no "ssl_certificate" is defined in server listening on SSL port while SSL handshaking, client: 203.0.113.10, server: 0.0.0.0:443这条错误的意思是:一个 TLS 客户端连到了 SSL 端口上某个没配 ssl_certificate 的 server 块,Nginx 没有证书可出示,只好中断握手。 多数情况下这个 server 块就是 443 端口的默认站点,它会接收用 IP 访问、不带 SNI 或访问未配置域名的连接。
快速修复
修法是给没有证书的 server 块补上证书,或者让它拒绝 TLS 握手。先列出 443 端口上的所有 server 块,看哪个没有证书:
sudo nginx -T 2>/dev/null | grep -nE "server_name|listen .*443|ssl_certificate |default_server"
然后要么给这个 server 块配证书,要么让兜底 server 明确拒绝 TLS 握手(Nginx 1.19.4 及以上):
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
ssl_reject_handshake on;
}
执行 sudo nginx -t && sudo systemctl reload nginx。用 SSL 检测工具可以看到每个域名现在拿到的是哪张证书。
原因
最常见的是 443 端口的默认 server 没有证书(原因 1 和 2),其余 3 种是证书指令写错了地方。
1. 兜底或默认 server 没有证书
只要有一个 server 写了 listen 443 ssl,同一地址和端口上的所有 server 都要处理 TLS。从 80 端口模板复制来的 server_name _; 兜底块,或者发行版自带的默认站点,往往没有证书。
- 确认: 报错里的
server:是0.0.0.0:443或[::]:443,nginx -T里能找到该端口上没有ssl_certificate的 server 块。 - 修复: 给它加
ssl_reject_handshake on;,用不到的话直接删掉。
2. 客户端不发送 SNI,或直接用 IP 访问
扫描器、健康检查、很老的客户端连接时不带 SNI,Nginx 无法选出对应的站点,只能交给默认 server。
- 确认:
openssl s_client -connect 203.0.113.10:443 -noservername失败,加上-servername example.com就正常。 - 修复: 如果确实希望 IP 访问也落到某个站点,就把它标成
default_server;否则保留拒绝握手的兜底块。负载均衡(如阿里云 SLB、腾讯云 CLB)的健康检查改成带域名的 HTTP 检查,或者检查后端的 80 端口。
3. 证书指令写错了位置
ssl_certificate 只能写在 http 或 server 里,不会从别的 server 块继承,也不能写在 location 里。
- 确认: 证书只写在了同名的另一个 server 块里,或者写在一个没有被 include 的文件里(
nginx -T看不到它)。 - 修复: 把指令挪到
server_name对应的那个 server 块,或者在那里 include 进来。
4. 同一个域名有两个 server 块
Certbot 的 Nginx 插件之类的工具只改其中一个块。如果 443 上有两个相同 server_name 的块,Nginx 只用第一个,并给出警告。
- 确认:
nginx -t打印conflicting server name "example.com" on 0.0.0.0:443, ignored。 - 修复: 合并成一个块,同一个域名和端口只保留一处证书配置。
5. 证书路径里用了变量
Nginx 1.15.9 起 ssl_certificate 可以包含变量。变量解析为空或文件不存在时,这些域名的握手会失败。
- 修复: 检查设置变量的 map,并提供默认值。
各环境修法
这条日志在各环境里都指向一个没有证书的 443 server 块,补上证书或加 ssl_reject_handshake on; 即可。
宝塔面板: 一个站点关闭 SSL 后,其配置里如果还残留 listen 443 ssl,或者手动改过默认站点配置,就会出现这条日志。在站点设置里重新保存一次 SSL 配置,或在“网站 → 默认站点”中指定一个已配好证书的站点,步骤见 宝塔面板配置 SSL。
Debian / Ubuntu 软件包: /etc/nginx/sites-enabled/default 默认只监听 80,但被改过的副本常常加了 443。禁用它,或者加上 ssl_reject_handshake on;。
Docker / Nginx Proxy Manager: 自动生成的默认 server 可能没有证书,在代理设置里配置默认证书或拒绝握手的默认站点。
Kubernetes ingress-nginx: 控制器会给未知主机名返回自带的假证书,所以很少出现这条日志,未知域名看到证书不匹配是正常的。
Nginx 1.19.4 之前的版本: 没有 ssl_reject_handshake,可以给兜底块配一张自签名证书,并 return 444; 直接断开连接。
不要这样做
这些做法会制造新问题,或者漏掉实际的故障。
- 把真实站点的证书挂在兜底 server 上应付所有未知域名。 走错站点的访客会看到证书不匹配,扫描 IP 的人还能借此知道你有哪些域名。
- 从
listen里去掉ssl让报错消失。 端口会用明文 HTTP 回应 TLS 客户端,所有 HTTPS 访问都会失败。 - 完全无视这条日志。 多数时候是噪音,但正式站点在改配置时丢了证书,也会报同样的错。
常见问题
网站访问正常,日志里却一直刷这个错,要紧吗?
一般不要紧。你的站点自己有证书,但扫描器和爬虫用 IP 或乱填的域名访问,落到了没有证书的默认 server 上。对正常访客无害,只是日志噪音。加一个写了 ssl_reject_handshake on; 的兜底 server 就不会再刷。
可以把 ssl_certificate 写在 http 块里吗?
可以。http 块里的指令会被所有没单独设置的 server 继承,相当于给每个 SSL server 一张兜底证书。但访问错站点的客户端会看到域名不匹配,而不是握手失败。
国内云服务器上,用 IP 访问 443 会有什么额外问题?
未备案域名解析到国内云服务器时,80/443 端口的访问可能被云厂商拦截,这和本报错无关。用 IP 访问 HTTPS 本来就拿不到匹配的证书,兜底 server 直接拒绝握手是最干净的做法。