# Tomcat 7升9,一个EncodingFilter搞崩了全站CSS
Tomcat 7升9一个EncodingFilter搞崩了全站CSS非科班野生程序员深耕政务信息化20年。自研Java Web框架跑了十几年一直在Tomcat 7上稳稳当当。信创改造要求升级到Tomcat 9换了war包一启动CSS全崩了、JS全废了、页面光秃秃只剩文字。排查下来罪魁祸首是一个存在了十几年的EncodingFilter。这篇文章记录这次升级踩的坑。最后感谢豆包、智谱、OpenCode决策是我做的代码是我搓的文字是他们总结的。现象Tomcat 7 → Tomcat 9应用代码一行没改部署启动登录页面出来了但CSS 没加载——样式全丢JS 没执行——按钮事件不响应页面就剩一堆裸奔的 HTML 文本打开浏览器开发者工具Network面板里CSS和JS请求全是200但 Content-Type 全是application/json;charsetUTF-8。浏览器看到application/json当然不按CSS/JS解析了。问题定位框架里有个EncodingFilter拦截/*十几年前写的publicvoiddoFilter(ServletRequestsrequest,ServletResponsesresponse,FilterChainchain)throwsIOException,ServletException{HttpServletRequestrequest(HttpServletRequest)srequest;XssHttpServletRequestWrapperxssRequestnewXssHttpServletRequestWrapper(request);xssRequest.setCharacterEncoding(targetEncoding);// 问题在这里对所有响应都设了 Content-Typesresponse.setContentType(application/json;charsetUTF-8);// CORS 头处理...chain.doFilter(xssRequest,sresponse);}第54行sresponse.setContentType(application/json;charsetUTF-8)——对所有请求包括CSS、JS、图片全设了application/json。为什么在 Tomcat 7 上没问题因为 Tomcat 7 的 DefaultServlet 处理静态资源时会在响应里覆盖Content-Type。CSS文件会被设成text/cssJS文件会被设成application/javascript。Filter 设的值被 DefaultServlet 覆盖了所以没问题。Tomcat 9 行为变了。Tomcat 9 对静态资源的处理更严格如果 Filter 已经设了 Content-TypeDefaultServlet不再覆盖。于是 Filter 设的application/json就原样返回给了浏览器。十几年没问题的代码容器一升级就炸了。修复判断URI后缀只对动态资源设 Content-TypepublicvoiddoFilter(ServletRequestsrequest,ServletResponsesresponse,FilterChainchain)throwsIOException,ServletException{HttpServletRequestrequest(HttpServletRequest)srequest;XssHttpServletRequestWrapperxssRequestnewXssHttpServletRequestWrapper(request);xssRequest.setCharacterEncoding(targetEncoding);Stringurirequest.getRequestURI();booleanisStaticResourceuri.endsWith(.css)||uri.endsWith(.js)||uri.endsWith(.png)||uri.endsWith(.jpg)||uri.endsWith(.gif)||uri.endsWith(.html)||uri.endsWith(.woff)||uri.endsWith(.ttf)||uri.endsWith(.eot)||uri.endsWith(.jsp);if(!isStaticResource){sresponse.setContentType(application/json;charsetUTF-8);}// CORS 头处理...chain.doFilter(xssRequest,sresponse);}这不只是EncodingFilter的问题修完这个CSS回来了。但Tomcat 7升9坑不止这一个。趁这个机会把这次升级踩的所有坑都列出来。坑1web.xml 版本声明Tomcat 7 用 Servlet 3.0Tomcat 9 支持 Servlet 4.0。web.xml 的版本声明要改!-- Tomcat 7 --web-appxmlnshttp://java.sun.com/xml/ns/javaeeversion3.0!-- Tomcat 9 --web-appxmlnshttp://xmlns.jcp.org/xml/ns/javaeexmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsdversion4.0不改的话某些新特性不生效而且 Tomcat 9 会按旧版规范解析行为可能不一致。坑2EL表达式严格模式Tomcat 9 的 EL 解析更严格。以前#{}和${}混用不出错Tomcat 9 直接报异常。框架里 JSP 页面如果同时用了#{}延迟表达式和${}立即表达式升级后会抛ELException。修法要么统一用${}要么在 web.xml 里加jsp-configjsp-property-groupurl-pattern*.jsp/url-patternel-ignoredfalse/el-ignored/jsp-property-group/jsp-config坑3Cookie SameSite 属性Tomcat 9 对 Cookie 的处理更安全了默认不再允许跨站发送 Cookie。政务系统里如果前端和后端不在同一个域比如前端走Nginx代理Session Cookie 可能被浏览器拦截。JSESSIONID 发不出去登录就失效。修法在context.xml里配置ContextCookieProcessorclassNameorg.apache.tomcat.util.http.Rfc6265CookieProcessorsameSiteCookieslax//Context坑4SetCharacterEncodingFilter 的顺序web.xml 里我后来加了一个 Tomcat 自带的SetCharacterEncodingFilterfilterfilter-namesetCharacterEncodingFilter/filter-namefilter-classorg.apache.catalina.filters.SetCharacterEncodingFilter/filter-class/filterfilter-mappingfilter-namesetCharacterEncodingFilter/filter-nameurl-pattern/*/url-pattern/filter-mapping注意它在 web.xml 的最底部。Tomcat 7 对 filter-mapping 的顺序不太敏感Tomcat 9 严格按声明顺序执行。如果这个 Filter 在 EncodingFilter 之前执行编码可能被覆盖。经验多个 Filter 拦截/*时顺序就是命运。web.xml 里谁先声明谁先执行Tomcat 9 不会帮你调整。坑5静态资源缓存 Filter 的交互框架里还有个CacheFilter给图片、CSS设缓存头filter-mappingfilter-namecache/filter-nameurl-pattern*.css/url-pattern/filter-mappingTomcat 7 时CacheFilter → EncodingFilter → DefaultServlet各管各的没问题。Tomcat 9 时EncodingFilter 如果对静态资源也设了 Content-Type会和 CacheFilter 的缓存行为冲突——浏览器缓存了Content-Type: application/json的CSS文件后续请求直接从缓存读错误内容。修了 EncodingFilter 之后CacheFilter 的缓存内容也需要清一遍。经验教训1. Filter 不应该对静态资源动手动脚。当年写 EncodingFilter 时根本没考虑静态资源反正 Tomcat 7 会兜底覆盖。这个兜底在 Tomcat 9 上没了。2. 容器升级不能只换war包。Tomcat 7→9 跨了两个大版本Servlet 规范从 3.0 到 4.0很多约定变成了规范以前靠容器容错的代码全部暴露。3. 跑了十几年的代码不代表没问题。只是一直没遇到触发条件。换个容器、换个JDK、换个运行环境隐性的坑全冒出来。4. 升级前先跑一遍 Filter 链的梳理。哪些 Filter 拦截/*、每个 Filter 对 Response 做了什么、对静态资源有没有副作用——列清楚再升级。小结Tomcat 7升9不是换个war包的事。十几年的老框架在旧容器上没问题的代码在新容器上可能是定时炸弹。EncodingFilter 对静态资源设了application/jsonTomcat 7 默默帮你覆盖了Tomcat 9 不帮了全站CSS就崩了。修起来不难难的是知道要修。老项目容器升级大家还踩过什么坑评论区交流。标签#Tomcat #容器升级 #Filter #Servlet #EncodingFilter #CSS失效 #老项目维护 #政务信息化