在生产环境中配置 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 配置的常见问题主要集中在以下几个方面:
- 证书链:确保包含完整的中间证书
- 混合内容:所有资源使用 HTTPS
- HSTS:正确配置避免重定向循环
- 加密套件:使用现代安全的配置
- 权限:私钥文件权限必须正确
通过本文的排查方法,可以快速定位并解决大部分 SSL 配置问题。
参考来源#