在生产环境中配置 HTTPS 时,经常会遇到各种问题。本文总结 Nginx SSL 配置中的常见错误及其排查方法,帮助你快速定位和解决问题。

1. 证书链顺序错误

常见症状

浏览器显示证书链不完整或提示「此证书链不受信任」:

1
2
# 使用 openssl s_client 查看证书链
echo | openssl s_client -connect example.com:443 -showcerts 2>/dev/null | grep -E "subject=|issuer="

问题原因

Nginx 配置的证书文件只包含服务器证书,没有包含中间证书(Intermediate Certificate)。

解决方法

将证书链按正确顺序拼接:服务器证书 → 中间证书 → 根证书(可选)。

1
2
3
4
# 正确的证书链顺序
cat server.crt > nginx-ssl.crt
cat intermediate.crt >> nginx-ssl.crt
# 根证书通常不需要添加

Nginx 配置:

1
2
3
4
5
6
7
8
9
server {
    listen 443 ssl;
    server_name example.com;
    
    ssl_certificate /path/to/nginx-ssl.crt;
    ssl_certificate_key /path/to/server.key;
    
    # ... 其他配置
}

验证命令

1
2
3
4
# 检查证书链是否完整
echo | openssl s_client -connect example.com:443 -showcerts 2>/dev/null | grep -c "CERTIFICATE"

# 理想情况下应该显示完整的证书链(3个证书:服务器+中间+根)

2. 混合内容问题

常见症状

页面显示「混合内容」警告,浏览器锁图标变为带感叹号的三角:

1
2
3
# Chrome 控制台提示
Mixed Content: The page at 'https://example.com' was loaded over HTTPS, 
but requested an insecure resource 'http://cdn.example.com/js/app.js'.

问题原因

HTTPS 页面加载了 HTTP 资源,浏览器会阻止或警告。

解决方法

方法一:将所有资源链接改为 HTTPS

1
2
<!-- 改为 -->
<script src="https://cdn.example.com/js/app.js"></script>

方法二:使用协议相对 URL

1
2
<!-- 自动使用当前页面的协议 -->
<script src="//cdn.example.com/js/app.js"></script>

方法三:配置 Nginx 自动将 HTTP 重定向到 HTTPS

1
2
3
4
5
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

方法四:内容安全策略(CSP)头

1
add_header Content-Security-Policy "upgrade-insecure-requests";

这个响应头会让浏览器自动将所有 HTTP 请求升级为 HTTPS。

验证命令

1
2
3
4
5
# 使用 curl 检查混合内容
curl -sI https://example.com | grep -i "content-security-policy"

# 使用在线工具检查
# https://www.mixedcontentchecker.com/

3. HSTS 配置问题

常见症状

配置 HSTS 后,浏览器报错「ERR_TOO_MANY_REDIRECTS」或「Redirect loop」。

问题原因

HSTS 告诉浏览器只能通过 HTTPS 访问,但如果同时配置了 HTTP 到 HTTPS 的重定向,可能会导致重定向循环。

解决方法

正确的 HSTS 配置顺序:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
server {
    listen 80;
    server_name example.com;
    
    # HTTP 重定向到 HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com;
    
    # HSTS 配置(注意:只在 HTTPS 站点配置)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    
    # ... 其他配置
}

常见 HSTS 配置选项

1
2
3
4
5
6
7
8
# 基础配置:一年有效
add_header Strict-Transport-Security "max-age=31536000" always;

# 包含子域名
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

# 预加载(需要到 hstspreload.org 提交)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

验证命令

1
2
3
4
5
# 检查 HSTS 头
curl -sI https://example.com | grep -i "strict-transport"

# 预期输出
# Strict-Transport-Security: max-age=31536000; includeSubDomains

4. 协议与加密套件配置错误

常见症状

  • 某些客户端无法连接
  • 提示「no ciphers available」或「no protocols available」
  • 安全性扫描显示「脆弱的加密算法」

问题原因

加密套件配置过于宽松或过于严格。

解决方法

推荐的现代加密套件配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
server {
    listen 443 ssl;
    
    # 只允许 TLS 1.2 和 TLS 1.3
    ssl_protocols TLSv1.2 TLSv1.3;
    
    # 现代加密套件配置
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
    
    # 优先使用服务器端的加密套件顺序
    ssl_prefer_server_ciphers off;
    
    # ... 其他配置
}

TLS 1.3 专用配置

1
2
3
# 如果只需要 TLS 1.3
ssl_protocols TLSv1.3;
ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256';

验证命令

1
2
3
4
5
6
7
8
9
# 检查支持的协议
openssl s_client -connect example.com:443 -tls1_2 2>&1 | grep "Protocol"
openssl s_client -connect example.com:443 -tls1_3 2>&1 | grep "Protocol"

# 检查支持的加密套件
openssl s_client -connect example.com:443 -cipher 'ECDHE-RSA-AES256-GCM-SHA384' 2>&1 | grep "Cipher"

# 使用 nmap 检查(如果安装)
# nmap --script ssl-enum-ciphers -p 443 example.com

5. 常见错误信息与解决方案

错误:「nginx: [emerg] no “ssl_certificate” is defined」

原因:443 端口的 server 块缺少 ssl_certificate 配置。

解决:添加证书路径配置。

1
2
3
4
5
server {
    listen 443 ssl;
    ssl_certificate /path/to/certificate.crt;
    ssl_certificate_key /path/to/private.key;
}

错误:「SSL_do_handshake() failed」

原因:可能是证书文件权限问题或私钥不匹配。

解决:检查文件权限和证书匹配。

1
2
3
4
5
6
7
# 检查文件权限(私钥必须是 600 或 400)
chmod 600 /path/to/private.key

# 验证证书和私钥匹配
openssl x509 -noout -modulus -in certificate.crt | openssl md5
openssl rsa -noout -modulus -in private.key | openssl md5
# 两个 MD5 值必须相同

错误:「ssl_certificate_key: no such file or directory」

原因:私钥文件路径错误或文件不存在。

解决:确认文件路径正确。

1
2
3
4
# 验证文件存在
ls -la /path/to/private.key

# 重新生成如果丢失(需要重新申请证书)

错误:「client sent “http” request, but server is in “ssl” mode」

原因:HTTP 请求发到了 HTTPS 端口。

解决:确保客户端使用 HTTPS,或添加 HTTP 重定向。

1
2
3
4
server {
    listen 80;
    return 301 https://$host$request_uri;
}

6. 调试技巧

使用 openssl s_client 测试连接

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 查看完整握手信息
openssl s_client -connect example.com:443 -verbose

# 测试特定密码套件
openssl s_client -connect example.com:443 -cipher 'ECDHE-RSA-AES256-GCM-SHA384'

# 测试特定 TLS 版本
openssl s_client -connect example.com:443 -tls1_2

# 查看证书详情
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -text

使用 curl 测试

1
2
3
4
5
6
7
8
# 显示详细的 SSL 信息
curl -v https://example.com 2>&1 | grep -E "SSL|TLS|HTTP"

# 检查响应头
curl -sI https://example.com | grep -iE "strict-transport|content-security|ssl"

# 忽略证书错误(仅用于测试)
curl -k https://example.com

Nginx 配置测试

1
2
3
4
5
6
7
8
# 测试配置文件语法
nginx -t

# 指定配置文件测试
nginx -t -c /path/to/nginx.conf

# 查看配置加载后的实际参数
nginx -T | grep -A 20 "server_name example.com"

7. 快速检查清单

部署 SSL 后,使用以下命令快速检查:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 1. 检查证书是否过期
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates

# 2. 检查证书链完整性
echo | openssl s_client -connect example.com:443 2>/dev/null | grep -c "Certificate chain"

# 3. 检查支持的 TLS 版本
echo | openssl s_client -connect example.com:443 2>/dev/null | grep "Protocol"

# 4. 检查 HSTS 头
curl -sI https://example.com | grep Strict-Transport

# 5. 检查混合内容
# 访问 https://www.mixedcontentchecker.com/

总结

Nginx SSL 配置的常见问题主要集中在以下几个方面:

  1. 证书链:确保包含完整的中间证书
  2. 混合内容:所有资源使用 HTTPS
  3. HSTS:正确配置避免重定向循环
  4. 加密套件:使用现代安全的配置
  5. 权限:私钥文件权限必须正确

通过本文的排查方法,可以快速定位并解决大部分 SSL 配置问题。


参考来源