# no "ssl_certificate" is defined：Nginx 默认站点没配证书

> Nginx 日志出现 no ssl_certificate is defined in server listening on SSL port，说明 TLS 握手落到了没配证书的 443 server 块，多是默认站点。补证书或拒绝握手即可。

Source: https://ssl.sh/zh/errors/nginx-no-ssl-certificate-defined/
Last updated: 2026-11-10

**这条错误的意思是：一个 TLS 客户端连到了 SSL 端口上某个没配 `ssl_certificate` 的 server 块，Nginx 没有证书可出示，只好中断握手。** 多数情况下这个 server 块就是 443 端口的默认站点，它会接收用 IP 访问、不带 SNI 或访问未配置域名的连接。

## 快速修复

修法是给没有证书的 server 块补上证书，或者让它拒绝 TLS 握手。先列出 443 端口上的所有 server 块，看哪个没有证书：

```bash
sudo nginx -T 2>/dev/null | grep -nE "server_name|listen .*443|ssl_certificate |default_server"
```

然后要么给这个 server 块配证书，要么让兜底 server 明确拒绝 TLS 握手（Nginx 1.19.4 及以上）：

```nginx
server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    ssl_reject_handshake on;
}
```

执行 `sudo nginx -t && sudo systemctl reload nginx`。用 [SSL 检测工具](https://ssl.sh/zh/tools/ssl-checker/)可以看到每个域名现在拿到的是哪张证书。

## 原因

最常见的是 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](https://ssl.sh/zh/glossary/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](https://ssl.sh/zh/guides/bt-panel-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`，可以给兜底块配一张[自签名证书](https://ssl.sh/zh/tools/self-signed-certificate-generator/)，并 `return 444;` 直接断开连接。

## 不要这样做

这些做法会制造新问题，或者漏掉实际的故障。

- **把真实站点的证书挂在兜底 server 上应付所有未知域名。** 走错站点的访客会看到证书不匹配，扫描 IP 的人还能借此知道你有哪些域名。
- **从 `listen` 里去掉 `ssl` 让报错消失。** 端口会用明文 HTTP 回应 TLS 客户端，所有 HTTPS 访问都会失败。
- **完全无视这条日志。** 多数时候是噪音，但正式站点在改配置时丢了证书，也会报同样的错。
