bigywater.tech
Spring · 113

安全响应头与 HTTPS 重定向

Spring Security 默认给你加了哪些响应头、漏了哪些?HSTS 什么时候才会出现?怎么把 HTTP 重定向到 HTTPS?放在 Nginx 后面为什么一直 302?这篇在 Spring Boot 4.0.8、Spring Security 7 上起真正的 TLS 和反向代理场景逐项实测。

没有 CSP
默认响应头里没有 content-security-policy,要自己配
启动失败
requiresChannel(...requiresSecure()) 在 Spring Security 7.0.7 里启动即 NoClassDefFoundError
302
不配 server.forward-headers-strategy,带了 X-Forwarded-Proto: https 也被重定向

默认响应头

实测 · 本机执行输出
  GET /hello(带凭据) → HTTP 200;cache-control: no-cache, no-store, max-age=0, must-revalidate; expires: 0; pragma: no-cache; x-content-type-options: nosniff; x-frame-options: DENY; x-xss-protection: 0
  GET /hello(不带凭据) → HTTP 401;cache-control: no-cache, no-store, max-age=0, must-revalidate; expires: 0; pragma: no-cache; www-authenticate: Basic realm="Realm"; x-content-type-options: nosniff; x-frame-options: DENY; x-xss-protection: 0
  GET /nope(带凭据,不存在) → HTTP 404;cache-control: no-cache, no-store, max-age=0, must-revalidate; expires: 0; pragma: no-cache; x-content-type-options: nosniff; x-frame-options: DENY; x-xss-protection: 0
  GET /cached(带凭据,控制器自己设了 Cache-Control: max-age=60) → HTTP 200;cache-control: max-age=60; x-content-type-options: nosniff; x-frame-options: DENY; x-xss-protection: 0
  GET /a.txt(带凭据,classpath 的静态文件) → HTTP 200;accept-ranges: bytes; cache-control: no-cache, no-store, max-age=0, must-revalidate; expires: 0; last-modified: Thu, 22 Oct 2026 00:38:00 GMT; pragma: no-cache; x-content-type-options: nosniff; x-frame-options: DENY; x-xss-protection: 0

nosniff、X-Frame-Options: DENY、X-XSS-Protection: 0、no-store 一组缓存头;控制器自己设的 Cache-Control 不被覆盖;没有 CSP、HSTS(明文下)。

自定义与关闭

实测 · 本机执行输出
  自定义 CSP、frameOptions(sameOrigin)、referrerPolicy(same-origin)、permissionsPolicy → HTTP 200;cache-control: no-cache, no-store, max-age=0, must-revalidate; content-security-policy: default-src 'self'; expires: 0; permissions-policy: geolocation=(); pragma: no-cache; referrer-policy: same-origin; x-content-type-options: nosniff; x-frame-options: SAMEORIGIN; x-xss-protection: 0
  headers.disable() → HTTP 200;(没有安全相关的头)

CSP、Referrer-Policy、Permissions-Policy 要自己配;headers.disable() 去掉全部默认头。

HSTS 与重定向

实测 · 本机执行输出
  [secure] HTTPS GET /hello → HTTP 200;cache-control: no-cache, no-store, max-age=0, must-revalidate; expires: 0; pragma: no-cache; strict-transport-security: max-age=31536000 ; includeSubDomains; x-content-type-options: nosniff; x-frame-options: DENY; x-xss-protection: 0
  [secure] HTTP(端口映射 http→https)GET /hello?x=1 → HTTP 302;Location https://127.0.0.1:<https 端口>/hello?x=1;(没有安全相关的头)
  [secure-hsts] HTTPS GET /hello → HTTP 200;cache-control: no-cache, no-store, max-age=0, must-revalidate; expires: 0; pragma: no-cache; strict-transport-security: max-age=600; x-content-type-options: nosniff; x-frame-options: DENY; x-xss-protection: 0
  requiresChannel(c -> c.anyRequest().requiresSecure())(Spring Security 6 的老写法)→ Caused by: java.lang.NoClassDefFoundError: org/springframework/security/web/access/channel/ChannelEntryPoint | Caused by: java.lang.ClassNotFoundException: org.springframework.security.web.access.channel.ChannelEntryPoint

HSTS 只在 HTTPS 响应里;redirectToHttps 加 portMapper;requiresChannel 老写法 7.0.7 里启动失败。

反向代理

实测 · 本机执行输出
  [没有配置转发头策略] 不带 X-Forwarded-Proto → HTTP 302,Location https://127.0.0.1:9999/hello
  [没有配置转发头策略] 带 X-Forwarded-Proto: https → HTTP 302,Location https://127.0.0.1:9999/hello,HSTS 无
  [server.forward-headers-strategy=framework] 不带 X-Forwarded-Proto → HTTP 302,Location https://127.0.0.1:9999/hello
  [server.forward-headers-strategy=framework] 带 X-Forwarded-Proto: https → HTTP 200,Location -,HSTS max-age=31536000 ; includeSubDomains
  [server.forward-headers-strategy=native] 不带 X-Forwarded-Proto → HTTP 302,Location https://127.0.0.1:9999/hello
  [server.forward-headers-strategy=native] 带 X-Forwarded-Proto: https → HTTP 200,Location -,HSTS max-age=31536000 ; includeSubDomains

代理转发 X-Forwarded-Proto,Boot 配 forward-headers-strategy,否则一直 302。

分步演示

安全响应头
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动(Spring Security 7.0.7,内嵌 Tomcat)。所有接口要 Basic 认证(alice / pw);TLS 用 keytool 生成的自签 PKCS12 证书,Python 客户端不校验证书;额外的 HTTP 连接器通过 WebServerFactoryCustomizer 加;反向代理场景只用 Python 手工带 X-Forwarded-Proto,没有起真正的 Nginx;端口映射到的 9999 没有进程监听,只看 Location
    • 没有验证:真实浏览器对 HSTS、CSP 的处理;HSTS preload;CSP 的 report-only 与 nonce;Spring Security 6 的 requiresChannel 为什么在 7 里还留着方法但缺类(只查了 jar 里没有这些类);redirectToHttps 在没有 portMapper 时对非标准端口的行为;redirectToHttps 与 HSTS 在 307 / 308 重定向方面的区别;真实 Nginx / 云负载均衡;server.tomcat.remoteip.* 的细项配置;X-Forwarded-Host 与 X-Forwarded-Port
    京ICP备2026067502号-1京公网安备11010502063487号