Nginx 如何强制(重定向到)HTTPS?
在监听 80 的 server 内用 location / 返回到已允许的 HTTPS 规范域名;健康检查可另设精确 location;配合 HSTS 头让浏览器之后直连 HTTPS。本文给出完整配置与证书、回环等注意事项。
标准做法:80 端口的 server 用 location / { return 301 https://example.com$request_uri; } 将业务请求跳转到已允许的 HTTPS 规范域名,保留路径与参数;443 的 server 块正常配 SSL 证书与业务。再加 HSTS 响应头,让浏览器后续直接走 HTTPS,连跳转都省掉。
完整配置
# HTTP:全部 301 到 HTTPS
server {
listen 80;
server_name example.com www.example.com;
location / { return 301 https://example.com$request_uri; }
}
# HTTPS:正常业务
server {
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# HSTS:让浏览器记住"以后只走 HTTPS"(确认全站 HTTPS 稳定后再开)
add_header Strict-Transport-Security "max-age=31536000" always;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme; # 让后端知道原始是 https
}
}
要点:固定 example.com 是已允许的规范域名;若需保留 $host,须明确允许的 host 范围并拒绝未知 Host。$request_uri 保留完整路径与参数。
注意事项
- 证书先就位:443 站点证书配好并验证后再开强制跳转,否则用户直接撞证书错误
- 健康检查例外:负载均衡器的 HTTP 健康检查被 301 会判失败,给检查路径开例外:
location = /healthz { return 200 "ok"; } # 放在 80 的 server 块,与 location / 并列;server 级 return 会先执行,无法被此例外绕过
- HSTS 谨慎开长 max-age:一旦下发,浏览器在 max-age 内只认 HTTPS,证书出问题用户没有任何绕过的余地
- 后端应用感知:
X-Forwarded-Proto头带给后端,并让后端只信任受控反向代理,否则客户端可伪造转发头;未配置时生成的重定向/链接可能还是 http(混合内容警告)
验证
curl -I 'http://example.com/some/page?a=1'
# HTTP/1.1 301 Moved Permanently
# Location: https://example.com/some/page?a=1
核对 Location 中的域名、路径和查询参数后,再访问跳转目标,检查证书与最终响应。分别测试业务路径和健康检查路径,避免只验证首页。
常见问题(FAQ)
Q:用 301 还是 302?
A:永久迁移用 301(浏览器会缓存跳转,SEO 权重传递);临时用途才 302。
Q:if ($scheme = http) 写法为什么不推荐?
A:官方 If Is Evil 主要讨论 location 内某些 rewrite 用法,并不是所有 if 都不安全;仅执行 return 的条件跳转可以有效。独立 80 server 块通常更清晰。
Q:API 接口也要强制跳转吗?
A:要,但注意 301 对 POST 的客户端重发行为不一;若需保留方法和请求体,可按客户端支持选择 307/308;敏感 API 宜直接要求客户端使用 HTTPS,重定向无法撤回首次明文传输。