游客发表
上周帮朋友处理一个电商站点的书部署 HTTPS 问题,打开浏览器直接跳“不安全”,书部署点开证书详情一看,书部署是书部署自签名的。他以为在 Nginx 里写上 ssl_certificate 就完事了,书部署结果证书链没配全,书部署安卓端直接报错。书部署这种事太常见了——证书文件拿在手里,书部署部署时却总在细节上栽跟头。书部署
先说证书文件本身。书部署从 CA 机构下载下来的书部署通常是个压缩包,里面至少有两个东西:你的书部署域名证书(比如 example.com.crt)和中间证书(可能叫 ca_bundle.crt 或 chain.crt)。很多人只把域名证书丢进配置,书部署浏览器在 PC 上看着正常,书部署一到手机就出问题。书部署原因很简单:PC 浏览器缓存了中间证书,移动端没有。正确的做法是把域名证书和中间证书按顺序拼接成一个文件。Linux 下一条命令搞定:
cat example.com.crt ca_bundle.crt > fullchain.crt 注意顺序,域名证书在前,中间证书在后。反过来 Nginx 启动时不会报错,但握手阶段会失败。这个坑我踩过两次,每次都要翻日志才想起来。
接下来是 Nginx 配置。假设你已经把 fullchain.crt 和私钥 example.com.key 放到了 /etc/nginx/ssl/ 目录下,server 块大概长这样:
server { listen 443 ssl http2; server_name example.com www.example.com; ssl_certificate /etc/nginx/ssl/fullchain.crt; ssl_certificate_key /etc/nginx/ssl/example.com.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; add_header Strict-Transport-Security "max-age=31536000" always; location / { root /var/www/html; index index.html; } } 几个关键点。ssl_protocols 里别留 TLSv1.0 和 1.1,现在主流浏览器已经不再支持,留着反而拉低安全评级。ssl_ciphers 那行我习惯只保留 ECDHE 开头的套件,前向保密是底线。ssl_session_cache 对性能影响不小,特别是小内存 VPS,10MB 大概能存 4 万个会话,够用了。HSTS 头加上 always 参数,确保所有响应都带上,包括错误页。
配完别急着 nginx -s reload。先用 nginx -t 检查语法,通过后再重载。然后拿 openssl 验证一下证书链:
openssl s_client -connect example.com:443 -servername example.com 输出里找 “Verify return code” 那一行,如果是 0 (ok) 就没问题。如果是 20 或 21,说明中间证书缺失或顺序错了。我见过有人把根证书也拼进去,其实没必要,大部分客户端自己带根证书库。
还有个小细节:私钥权限。很多教程让你 chmod 600,这没错。但如果你用 certbot 自动续期,它生成的私钥默认是 600,Nginx 的 worker 进程以 www-data 或 nginx 用户运行,读不了。解决办法是把私钥属主改成 root,组改成 nginx,权限 640。或者干脆让 certbot 用 --deploy-hook 自动重载 Nginx,省得手动折腾。
最后说个真实案例。一个客户坚持用 ssl_certificate 指向单域名证书,中间证书死活不加。结果微信内置浏览器打开直接白屏,用户投诉到客服。我远程连上去,把两个文件一拼,重载,十秒钟解决。他问我为什么之前 PC 上没事,我说你清一下浏览器缓存再试。清完,PC 也报错了。他沉默了三秒,说了句“原来如此”。
部署 SSL 证书本身不复杂,复杂的是那些不报错但实际有问题的状态。Nginx 不会告诉你证书链不完整,它只会在特定客户端上悄悄失败。多测几个设备,多看日志,比任何教程都管用。
友情链接