hsts必须由https响应头发出且仅在https路径生效,python框架配置需配合ssl重定向与反向代理透传,nginx中须在443 server块用always参数稳定发送含preload的头,并确保全站https覆盖及301跳转正确。

HSTS 不是 Python 框架自己能“配出来”的东西——它必须由 HTTPS 响应头发出,且只在 HTTPS 请求路径上生效;Python 层加错位置、没配跳转、或被 Nginx 吞掉头,都会让 HSTS 彻底失效。
为什么 Flask/Django 加了 SECURE_HSTS_SECONDS 还是不生效
常见错误是以为设个配置就完事。Django 的 SECURE_HSTS_SECONDS 是开关,不是建议值:设为 0 就完全不发头;设为非零才启用。但它依赖另一个前提:SECURE_SSL_REDIRECT = True 必须开启,否则 HTTP 请求根本进不到 HTTPS 响应链路里,HSTS 头压根不会生成。
- Flask 默认不提供原生 HSTS 支持,硬塞
response.headers['Strict-Transport-Security']容易被中间件覆盖或重复发送 - 用
flask-talisman更可靠:初始化时传force_https=True和force_https_permanent=True,它会自动加 HSTS、X-Content-Type-Options等头 - 所有框架都要检查反向代理(如 Nginx)是否透传响应头:确认
proxy_pass_request_headers on,且没因underscores_in_headers off拦截下划线命名的头(比如某些旧版 Nginx 会把Strict-Transport-Security当非法头丢弃)
Nginx 配置 HSTS 头的正确位置和参数
HSTS 头必须只出现在 HTTPS 响应中,HTTP 响应里加了也白加,浏览器直接忽略。不能写在顶层 server 块里“一刀切”,更不能用 if ($scheme = https) { } 包裹——Nginx 官方明确说这种用法在 location 外不可靠。
- 只在监听 443 端口、启用 SSL 的
server块里加:add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; -
always参数关键:确保 404、500、304 等所有 HTTPS 响应都带这个头,否则策略可能中断 - HTTP server 块(监听 80)只做一件事:
return 301 https://$host$request_uri;,不要加任何 HSTS 相关内容 - 避免用
$http_host构造跳转地址,改用$host,防 Host 头注入
为什么本地开发 localhost 加了 HSTS 也看不到效果
浏览器对 localhost 不豁免证书校验,也不接受 HSTS 策略——这是硬性限制。自签名证书必然触发 NET::ERR_CERT_AUTHORITY_INVALID,这不是配置问题,是设计如此。
- Chrome 地址栏输入
thisisunsafe(无空格、无回车)可临时跳过警告页;Edge 是badidea - Mac 可用
security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain cert.pem把证书加进系统信任库(需重启浏览器) - 但 HSTS 在 localhost 永远不会被浏览器记住,
chrome://net-internals/#hsts查不到记录,别在这儿测 HSTS 生效逻辑 - 真正验证要换真实域名 + Let's Encrypt 证书 + 真实 HTTPS 访问
提交 HSTS Preload 列表前必须踩过的坑
加了 preload 参数,等于主动申请把自己域名写进 Chrome/Firefox 内置列表。一旦收录,哪怕你之后关掉 HTTPS,用户半年内都打不开网站——这不是警告,是事实。
- 主域名和所有声明的子域(如
api.example.com、cdn.example.com)都必须可 HTTPS 访问,且证书有效、SAN 匹配、未过期 - 所有 HTTPS 响应(含错误页)必须返回完整头:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload - 80 端口必须返回 301(不是 302),且跳转目标不含端口(如
https://example.com:443/会失败) - 用
curl -I https://example.com检查响应头,再用隐身模式访问http://example.com确认跳转路径正确——注意:Chrome 对已预载域名会直接阻断 HTTP 请求,不发包,所以测试要用未预载的新域名
最容易被忽略的一点:HSTS 不解决首次访问风险,它只保护“已知用户”。没配好 301 跳转,第一次输 http:// 仍走明文——这时候中间人已经能劫持并阻止 HSTS 头下发。安全链条里,跳转和 HSTS 是两环,缺一不可。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











