Nginx 如何去掉 www 并同时跳转到 HTTPS?
Nginx 一步完成"去 www + 强制 HTTPS":www 的 80/443 两个 server 块都 301 到 https://裸域;附四种入口组合表、证书要求(覆盖 www 与裸域)与 FAQ。
四个入口(http/https × www/裸域)都要照顾:www 变体无论 80 还是 443 都 return 301 https://裸域$request_uri;裸域 80 跳 443;只有裸域 443 真正提供服务。
完整配置
# ① http + www → https 裸域
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
# ② http + 裸域 → https 裸域
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
# ③ https + www → https 裸域(证书要先握手成功!)
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
return 301 https://example.com$request_uri;
}
# ④ https + 裸域:真正干活的 server 块
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
root /var/www/site;
index index.html;
location / { try_files $uri $uri/ =404; }
}
两个最容易漏的点
- ③ 不能省:用户直接敲
https://www.example.com时,HTTPS 握手发生在跳转之前——没有 ③,用户先看到证书错误或另一个站点的内容。而且证书必须同时覆盖www.example.com和example.com(一张证书两个 SAN),否则握手阶段就失败,跳转根本没机会执行。 - 合并写法可行但要小心:
server_name www.example.com example.com; if ($host = www.example.com) {...}这种写法引入 if 嵌套,Nginx 社区共识是 server 块拆分更清晰,别省这几个块。
申请双名证书:
certbot --nginx -d example.com -d www.example.com
验证四路跳转
curl -I http://www.example.com/path?q=1 # → https://example.com/path?q=1
curl -I http://example.com/path?q=1 # → https://example.com/path?q=1
curl -I https://www.example.com/path?q=1 # → https://example.com/path?q=1
curl -I https://example.com/path?q=1 # → 200
四条链各跳一次就到终点,无循环、无多跳。
观测云对照
四类入口的流量结构是检验跳转配置的最佳数据:把访问日志接入观测云,按 host × 状态码聚合——健康状态下 www 与 http 裸域上应只有 301,全部 200 集中在 https 裸域;哪个入口还有 200 流量,说明对应 server 块没配对。跳转异常、证书即将过期都可以通过监控器告警。
常见问题(FAQ)
Q:反过来要 www、跳裸域,怎么改?
A:逻辑完全对称:把 server_name 里的角色互换——裸域的块 301 到 https://www.example.com$request_uri,www 的 443 块做正式服务。
Q:多次跳转(http www → https www → https 裸域)有影响吗?
A:有。每多一跳多一次往返延迟,搜索引擎也不喜欢跳转链。上面的配置保证任意入口最多一跳到终点。
Q:HSTS 要不要一起上?
A:稳定运行 HTTPS 一段时间后再加 add_header Strict-Transport-Security "max-age=31536000" always;,加在 ④ 里。注意 HSTS 只记在浏览器侧,配错了要等缓存过期。