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 握手发生在跳转之前——没有 ③,用户先看到证书错误或另一个站点的内容。每个 HTTPS 入口的证书必须覆盖其主机名;示例共用证书时需同时覆盖两个名称,也可以分别使用各自有效的证书。 - 合并写法可行但要小心:
server_name www.example.com example.com; if ($host = www.example.com) {...}仅在 if 中 return 的写法可以有效工作;拆分 server 块便于清楚表达入口行为,并非所有 if 都有问题。
申请双名证书:
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' # 正式页面状态;不存在的路径可为404
前三类入口各跳一次,规范 HTTPS 裸域无需跳转;应无循环、无额外跳转。
验证四类入口时,分别检查首跳状态码、Location 和最终页面。观测云 HTTP 拨测可配置是否跟随重定向:检查首跳时关闭跟随,检查最终内容时再开启。保留路径和查询串的测试样例,不能只凭访问日志里 301 与 200 的比例判断跳转链正确。
常见问题(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 只记在浏览器侧,可在有效 HTTPS 响应中发送 max-age=0 清除访问到该响应的客户端策略;离线客户端、父域策略或预加载名单另有边界,不能立即让所有浏览器回退。