大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
HTTPS 与证书
这一课你将能做到
- 在
server中配置listen 443 ssl、ssl_certificate/ssl_certificate_key,并解释为何常用 fullchain - 写出 HTTP→HTTPS 与裸域→www 的 301,与
prod.conf.example对齐 - 口述 certbot 申请/续期后需要
nginx -t与 reload 的原因,并说明 后端是否还要 HTTPS
为什么要学
公网站点没有 TLS,浏览器警告、Cookie Secure、HSTS 都无从谈起。生产惯例是:在 Nginx 终止 TLS,再以 HTTP 访问本机 Spring / Next。
证书链不全、只开 443 忘了 80 跳转、裸域与 www 分裂,都会造成「我证书装了但用户仍进不去/不安全」。
本课把证书文件、跳转、与内网明文上游的边界讲清;安全头(HSTS)在下一课加强。
先建立图像
浏览器 ──TLS──► Nginx:443 ──http──► 127.0.0.1:3000 / :8080
│
├─ fullchain.pem(站点证 + 中间证)
└─ privkey.pem (私钥,权限要严)
80 端口:只负责 301 到 https://www…
人话:HTTPS 是「门岗与访客之间的加密对讲」;门岗进内院后,对知根知底的室友可以用普通对讲(本机 HTTP)。
| 文件 | 作用 |
|---|---|
fullchain.pem | 叶子证书 + 中间 CA,客户端靠它建链 |
privkey.pem | 私钥,绝不能进 git / 世界可读 |
cert.pem(仅叶子) | 缺中间链时部分客户端握手失败 |
跟着做
步骤 1:读官方 SSL 示例方向
浏览 Configuring HTTPS servers,记下最小要素:listen … ssl、证书、私钥。
用中文写:「TLS 在 server 层配置,不是 location 装饰。」
步骤 2:对照 443 主站骨架
server {
listen 443 ssl http2;
server_name www.coderoadmap.cn;
# Let's Encrypt 常见路径(生产取消注释并确保证书存在)
ssl_certificate /etc/letsencrypt/live/www.coderoadmap.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.coderoadmap.cn/privkey.pem;
# 现代浏览器:TLS1.2+;具体套件随版本演进
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
# location /api/ … location / … 见前几课
}
prod.conf.example 中证书行默认注释,避免无证书时 -t/启动失败;上线前打开并指向真实文件。
步骤 3:强制 HTTPS + 统一 www
# 所有 HTTP 进 www HTTPS
server {
listen 80;
server_name www.coderoadmap.cn coderoadmap.cn;
return 301 https://www.coderoadmap.cn$request_uri;
}
# 裸域 HTTPS 也归并到 www
server {
listen 443 ssl http2;
server_name coderoadmap.cn;
ssl_certificate /etc/letsencrypt/live/www.coderoadmap.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.coderoadmap.cn/privkey.pem;
return 301 https://www.coderoadmap.cn$request_uri;
}
应看到:canonical 固定为 **https://www.coderoadmap.cn**,与仓库示例一致。
步骤 4:certbot 工作流(示意)
# 按官方/发行版文档安装 certbot 与插件后:
sudo certbot certonly --nginx -d www.coderoadmap.cn -d coderoadmap.cn
sudo nginx -t && sudo systemctl reload nginx
# 续期后重载(deploy hook 示例)
# certbot renew --deploy-hook "systemctl reload nginx"
测试域见 deploy/nginx/test.conf.example。本地开发可用 mkcert 或纯 HTTP 直连应用端口。
步骤 5:用 openssl 看证书(可选)
openssl s_client -connect www.coderoadmap.cn:443 -servername www.coderoadmap.cn </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates
应看到:subject 与域名匹配、日期未过期。链不完整时,部分手机/旧客户端会失败而桌面浏览器却「看起来正常」——更要 fullchain。
步骤 6:后端还要不要 HTTPS?
本机或受信内网:proxy_pass http://127.0.0.1:8080 可接受。
跨主机明文:有被窃听/篡改风险,应内网 TLS、专线或 mesh。
不要把「外网已 HTTPS」误当成「内网永远安全」——边界变了,结论跟着变。
讲清楚要点
- 因为证书与私钥在 server 上下文,所以每个 HTTPS server_name 都要配齐路径(或共享同一证书 SAN)——裸域跳转块也要能握手。
- 因为缺中间证书会导致部分客户端失败,所以优先用 fullchain.pem——不要只挂叶子。
- 因为用户仍可能敲 http://,所以 80 端口应 301 到 https——仅开 443 不够友好。
- 因为 www 与裸域都可能被收藏,所以两边收敛到同一 canonical——cookie/缓存/SEO 一致。
- 因为证书会过期,所以续期后必须 reload Nginx 才能换上新证——hook 自动化。
- 因为 TLS 终止在边缘,所以应用可专注业务;但要信任 X-Forwarded-Proto——否则后端以为自己是 http。
- 因为官方 HTTPS 指南随版本更新套件建议,所以协议优先 TLS1.2/1.3,并跟进文档——勿长期钉死过时弱套件。
你可能会懵的地方
-
http2 会不会影响反代?
listen … http2是客户端到 Nginx;上游仍常是 HTTP/1.1(proxy_http_version 1.1)。两段协议可以不同。 -
HSTS 和 301 有什么差别?
301 是「这次告诉你去 https」;HSTS 是「以后浏览器自己强制 https」。HSTS 要在 HTTPS 稳定后再开(安全头课)。 -
测试环境没有域名怎么办?
hosts 指向、或自签/mkcert;不要把自签流程原样当生产 Let's Encrypt 流程。 -
证书路径
-t报错 permission denied?
Nginx worker 用户需能读证书文件(常见是 root 拥有、它人可读证书,私钥权限更严)。按发行版惯例修复,勿把私钥 chmod 成世界可读。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
只配置 cert.pem 无链 | 部分客户端握手失败 | 使用 fullchain.pem |
| 80 不跳转 | 用户停留明文 | return 301 https://… |
| 私钥提交进 git | 密钥泄露 | 仅服务器路径;密钥管理规范 |
| 续期后不 reload | 仍用旧证直至重启 | deploy-hook reload |
| 以为上游也必须 https | 同机复杂度无收益 | 本机 http + 转发头即可(边界内) |
自测三问
- 为什么推荐
fullchain.pem而不是只有叶子证书? prod.conf.example如何把coderoadmap.cn与www、http 与 https 统一?- TLS 在 Nginx 终止后,Spring Boot 如何知道原请求是 https?
小结一句话
TLS 在 Nginx 终结;fullchain + 301 统一到 www HTTPS,上游本机可用 HTTP。