首选 return 301,因其更轻量、执行早、无正则开销且避免循环重定向;rewrite 开销大、易出错,仅在需复杂路径处理时选用。

nginx 里用 return 301 还是 rewrite 做 HTTP→HTTPS 跳转?
直接用 return 301,别用 rewrite。前者更轻量、执行早、无正则开销,且不会因匹配逻辑出错导致循环重定向。
常见错误是写成:
rewrite ^(.*)$ https://$host$1 permanent;
这在 $host 含端口(如 example.com:8080)或带子域名时容易出问题;而 return 明确指定目标域名,可控性强。
正确写法示例(放在 HTTP server 块中):
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
- 确保
server_name和跳转目标域名一致,避免跨域跳转失败 -
$request_uri保留原始路径和查询参数,比$1更可靠 - 若需支持多个域名,每个都单独配
return,不要用变量拼接 host
Strict-Transport-Security 头该加在哪?max-age 怎么设?
HSTS 头必须由 HTTPS 响应返回,HTTP 响应里加了也无效。它不是跳转机制,而是告诉浏览器“接下来 N 秒内,对这个域名的所有请求,强制走 HTTPS,连跳转都不许走 HTTP”。
典型配置(放在 HTTPS server 块的 location / 或全局 add_header):
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
-
max-age=31536000是 1 年,上线稳定后建议设满;调试阶段可先用300(5 分钟)验证 -
includeSubDomains表示子域名也生效,但前提是所有子域名都已支持 HTTPS,否则会导致子域无法访问 -
preload是提交到浏览器 HSTS 预加载列表的前提,加了就不能临时撤回,务必确认全站 HTTPS 已 100% 覆盖 -
always参数很重要:没有它,3xx/4xx 响应默认不发 header,HSTS 就丢了
为什么加了 HSTS 还有用户访问 HTTP 版本?
因为 HSTS 不是即时生效的——浏览器第一次访问 HTTPS 时收到头才开始计时,之前所有 HTTP 请求照常发出。用户书签、历史记录、搜索引擎快照、手动输入 http://,都会触发 HTTP 请求。
也就是说:HSTS 不替代 301 跳转,两者必须共存。301 解决“当前这次请求”,HSTS 解决“未来几十分钟到一年内的后续请求”。
- 新用户首次访问,靠 301 把
http://拉过来 - 老用户再次访问,浏览器自动改发
https://,连服务器都收不到 HTTP 请求 - 如果只配 HSTS 不配 301,首次访问 HTTP 会失败(连接被浏览器直接拦截,显示 ERR_SSL_PROTOCOL_ERROR 类错误)
Cloudflare 或 CDN 后面怎么配 HSTS?
如果你用了 Cloudflare、阿里云 CDN、腾讯云 CDN 等,它们通常提供控制台开关或自定义响应头功能,不要在源站重复加 HSTS 头,否则可能叠加或冲突。
优先在 CDN 层统一配置,原因有三:
- CDN 节点离用户更近,HSTS 头下发更快
- 源站可能没暴露公网,或 HTTPS 终止在 CDN,源站实际走的是 HTTP 内网通信,此时源站加 HSTS 无效(浏览器根本收不到)
- Cloudflare 的 “Always Use HTTPS” 功能本质就是自动加 301 + HSTS,打开即生效,无需手动写规则
检查是否生效,用 curl 看响应头:
curl -I http://example.com
应该看到 301 Location → https,再对 https 地址执行:
curl -I https://example.com
确认返回头含 Strict-Transport-Security,且值与预期一致。
最易忽略的一点:HSTS 的 max-age 一旦下发,浏览器就记住了,即使你后来删掉 header,倒计时仍继续——想测试或回滚,只能等过期,或让用户清空浏览器 HSTS 缓存(Chrome 地址栏输 chrome://net-internals/#hsts)。


















