Strict-Transport-Security(HSTS)不生效?还是测试方式有误?
解决Jetty添加HSTS后浏览器仍可HTTP访问的问题
看起来你已经迈出了第一步——在Jetty里添加HSTS头并通过curl验证成功,但浏览器这边没按预期工作,这其实是HSTS的特性和配置细节导致的,我来帮你拆解问题并给出解决方案:
核心原因分析
HSTS(HTTP Strict Transport Security)的生效逻辑有几个关键前提,你可能刚好踩中了其中一点:
- HSTS头必须通过HTTPS返回:浏览器会直接忽略HTTP响应里的HSTS头,这是为了防止中间人攻击。如果你的测试是先访问HTTP,那浏览器根本不会记住这个规则;
- 需要先通过HTTPS访问一次:HSTS是浏览器端的规则,只有当用户(或测试者)通过HTTPS访问过你的站点,浏览器才会把这个域名加入强制HTTPS列表,后续的HTTP请求才会自动跳转到HTTPS;
- 头参数不完整:如果你的HSTS头缺少
max-age(必填)或者配置不正确,浏览器也不会生效这个规则。
具体修复步骤
1. 确保Jetty已配置HTTPS连接器
首先你得让Jetty支持HTTPS,没有SSL证书的话,HSTS完全发挥不了作用。你可以在代码里添加SSL连接器配置:
Server server = new Server(); // 配置HTTPS连接器 SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); sslContextFactory.setKeyStorePath("/path/to/your/keystore.jks"); sslContextFactory.setKeyStorePassword("your-keystore-password"); HttpConfiguration httpsConfig = new HttpConfiguration(); httpsConfig.addCustomizer(new SecureRequestCustomizer()); ServerConnector httpsConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), new HttpConnectionFactory(httpsConfig)); httpsConnector.setPort(443); server.addConnector(httpsConnector); // 保留HTTP连接器用于重定向 ServerConnector httpConnector = new ServerConnector(server); httpConnector.setPort(80); server.addConnector(httpConnector);
2. 正确设置完整的HSTS头
补全你的BlockingServlet代码,确保返回的HSTS头参数完整合规:
public class BlockingServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置HSTS头:有效期1年,包含子域名(可根据需求调整参数) response.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains"); response.getWriter().write("HSTS Test Page"); } }
注:
max-age单位为秒,includeSubDomains是可选参数,若你的子域名也需要强制HTTPS则添加。
3. 添加HTTP到HTTPS的重定向
HSTS是让后续访问强制HTTPS,但第一次访问HTTP时,需要主动重定向到HTTPS,不然浏览器还是会加载HTTP页面。你可以用Jetty的RewriteHandler实现:
RewriteHandler rewriteHandler = new RewriteHandler(); rewriteHandler.setRewriteRequestURI(true); rewriteHandler.setRewritePathInfo(true); // 添加HTTP转HTTPS的重定向规则 RedirectPatternRule redirectRule = new RedirectPatternRule(); redirectRule.setPattern("/*"); redirectRule.setLocation("https://your-domain.com$0"); // 替换为你的实际域名 rewriteHandler.addRule(redirectRule); // 关联你的Servlet处理器 ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); context.addServlet(new ServletHolder(new BlockingServlet()), "/*"); rewriteHandler.setHandler(context); server.setHandler(rewriteHandler);
4. 正确测试流程
- 启动配置完成的Jetty服务器;
- 先通过HTTPS访问你的站点(比如
https://localhost:443),让浏览器接收并保存HSTS规则; - 再尝试访问HTTP地址(比如
http://localhost:80),此时浏览器应该会自动跳转到HTTPS,或直接阻止HTTP访问。
为什么curl能抓到头但浏览器不行?
curl只是单纯读取响应头,不管是HTTP还是HTTPS返回的头都会显示;但浏览器会严格遵循HSTS规范:只有HTTPS返回的HSTS头才会被信任并生效,而且需要至少一次HTTPS访问来触发浏览器记录规则。
内容的提问来源于stack exchange,提问作者curiousengineer
相关产品推荐
相关产品推荐

