浏览器报“不安全”是因为缺少strict-transport-security(hsts)响应头,导致首次访问仍可能走http,存在中间人攻击风险;hsts必须由https响应返回,且max-age至少31536000秒,否则chrome视作无效。

为什么浏览器还在报“不安全”,明明已经配了 HTTPS
因为 HTTPS 只是传输层加密,浏览器判断“安全”的依据远不止证书有效——它还会检查响应头里有没有 Strict-Transport-Security(HSTS)。没这个头,用户首次访问仍可能走 HTTP,中间人攻击风险照旧。
常见错误现象:net::ERR_CERT_COMMON_NAME_INVALID 已解决,但地址栏仍显示“不安全”图标;手动输入 http:// 能打开网站;Chrome 开发者工具的 Security 标签页提示 “This site is not using HSTS”。
- HSTS 必须由 HTTPS 响应返回,HTTP 响应里加无效,且会被浏览器忽略
-
max-age至少设为31536000(1 年),低于300秒的值会被 Chrome 当作 0 处理 - 首次访问时若未带 HSTS 头,浏览器不会记住该站点需强制 HTTPS,后续哪怕你加了头,也要等用户下次访问才生效
- 测试阶段可先用
max-age=300+includeSubDomains+preload(仅当确认子域全支持 HTTPS 时再加preload)
Django / Flask 里怎么加 HSTS 和其他关键安全头
框架默认不发这些头,得自己加。别用中间件硬塞字符串,优先用框架原生支持的方式,避免被覆盖或重复发送。
以 Django 为例,SECURE_HSTS_SECONDS 是开关,不是“建议值”:设为 0 就完全不发头;设为非零才启用。同时必须开启 SECURE_SSL_REDIRECT = True,否则 HTTP 请求根本进不到 HSTS 生效路径。
立即学习“Python免费学习笔记(深入)”;
- Django:在
settings.py加SECURE_HSTS_SECONDS = 31536000、SECURE_HSTS_INCLUDE_SUBDOMAINS = True、SECURE_HSTS_PRELOAD = True(仅当你已提交到 HSTS Preload List 后才开) - Flask:用
flask-talisman,初始化时传force_https=True和force_https_permanent=True,它会自动加 HSTS、X-Content-Type-Options等头 - 所有框架都要注意:反向代理(如 Nginx)可能吞掉响应头,确认
proxy_pass配置里有proxy_pass_request_headers on,且没用underscores_in_headers off拦截下划线命名的头
Content-Security-Policy 头为什么一加就白屏
因为 CSP 是“默认禁止一切,只放行明确声明的资源”,写错一条规则,JS/CSS/图片/字体全被拦,页面直接白屏或功能异常。它不像 HSTS 那样“加了就安全”,而是“加错就崩溃”。
典型错误现象:控制台大量 Refused to load the script 'xxx' because it violates the following Content Security Policy directive;页面样式消失、按钮无响应、字体加载失败。
- 开发期先用
Content-Security-Policy-Report-Only替代Content-Security-Policy,配合report-uri或report-to收集违规日志,别直接上线阻断模式 -
script-src不能只写'self'—— 如果用了内联onclick或eval(),得加'unsafe-inline'或'unsafe-eval'(不推荐),更稳妥的是迁移到外部 JS 文件 +nonce或hash - 第三方服务(如 Google Analytics、CDN 字体)要显式列出域名,
https://fonts.googleapis.com和https://fonts.gstatic.com是两个不同源,都得写
Nginx 层面加安全头比应用层更可靠吗
更可靠,但代价是灵活性下降。Nginx 发头不依赖 Python 进程,即使应用挂了、WSGI 超时、Django 中间件出错,只要 Nginx 正常,头依然能发出去。
不过要注意:Nginx 的 add_header 默认不继承父级块,如果在 server 块里加了头,在 location 块里又写了另一个 add_header,父级头就丢了。这是最常被忽略的坑。
- 用
add_header时,在每个location块里重复写全量头,或改用more_set_headers(需安装headers-more-nginx-module)来支持继承 -
Strict-Transport-Security必须加在 HTTPS server 块里,HTTP server 块里加等于白加 - 别在 Nginx 里用
add_header X-Frame-Options "DENY"同时又在 Django 里用django.middleware.clickjacking.XFrameOptionsMiddleware,重复设置可能导致部分浏览器解析异常
真正难的不是加几个头,而是理解每个头在什么环节起作用、被谁覆盖、在哪一级失效。比如 HSTS 依赖浏览器缓存,CSP 依赖前端资源组织方式,X-Content-Type-Options 依赖响应体实际 MIME 类型是否匹配——这些细节一旦错位,安全策略就变成幻觉。










