启用HTTPS后Tomcat 10出现浏览器返回按钮表单重复提交问题
解决Tomcat启用HTTPS后返回按钮触发表单重复提交(ERR_CACHE_MISS)的问题
问题根源
启用HTTPS后,浏览器对POST请求的缓存策略会更严格,同时Tomcat的HTTPS连接器默认可能添加no-cache类响应头,导致POST提交后的页面无法被浏览器缓存。点击返回按钮时,浏览器无法从缓存加载上一页,只能重新发起POST请求,从而触发"Confirm Form Resubmission"提示。
解决方案
1. 采用PRG模式(POST-Redirect-GET)
这是解决表单重复提交最标准的方案:表单提交后,服务器处理完POST请求,不直接转发到结果页,而是通过重定向(sendRedirect)跳转到GET请求的页面。这样浏览器历史记录里保存的是GET请求,点击返回时只会加载这个GET页面,不会触发POST重提交。
示例代码(Servlet处理POST):
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 处理表单提交逻辑,比如保存数据 saveFormData(request); // 重定向到结果页(GET请求) response.sendRedirect(request.getContextPath() + "/success.jsp"); }
2. 调整Tomcat的响应缓存头配置
如果某些静态结果页需要被缓存,可以通过过滤器或页面设置修改响应头,允许浏览器缓存HTTPS下的页面:
- 通过Web Filter设置:
在web.xml中添加过滤器:
<filter> <filter-name>CacheControlFilter</filter-name> <filter-class>com.yourpackage.CacheControlFilter</filter-class> </filter> <filter-mapping> <filter-name>CacheControlFilter</filter-name> <url-pattern>/success/*</url-pattern> <!-- 匹配需要缓存的页面路径 --> </filter-mapping>
过滤器实现:
public class CacheControlFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; // 设置缓存策略:允许公开缓存,有效期1小时 response.setHeader("Cache-Control", "public, max-age=3600"); response.setHeader("Expires", String.valueOf(System.currentTimeMillis() + 3600 * 1000)); chain.doFilter(req, res); } }
- 直接在JSP页面设置:
在需要缓存的JSP头部添加:
<% response.setHeader("Cache-Control", "public, max-age=3600"); response.setHeader("Expires", String.valueOf(System.currentTimeMillis() + 3600 * 1000)); %>
3. 补充:添加表单Token防止重复提交(可选)
如果担心用户手动刷新页面导致重复提交,可以在表单中添加唯一Token,服务器验证Token有效性:
<form method="post" action="submit"> <input type="hidden" name="token" value="${sessionScope.token}"> <!-- 其他表单字段 --> <button type="submit">提交</button> </form>
Servlet生成和验证Token:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 生成唯一Token并存入Session String token = UUID.randomUUID().toString(); request.getSession().setAttribute("token", token); request.getRequestDispatcher("form.jsp").forward(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String requestToken = request.getParameter("token"); String sessionToken = (String) request.getSession().getAttribute("token"); if (requestToken != null && requestToken.equals(sessionToken)) { // 处理表单提交 saveFormData(request); // 移除Token,防止重复使用 request.getSession().removeAttribute("token"); response.sendRedirect("success.jsp"); } else { // 重复提交,返回错误提示 response.sendRedirect("error.jsp"); } }
关键注意点
- PRG模式是解决返回按钮问题的核心,其他方案仅作为补充。
- HTTPS下不要强制缓存敏感页面,避免安全风险。
内容的提问来源于stack exchange,提问作者Feng Zhang
相关产品推荐
相关产品推荐

