/*
Filter in Tomcat? A Deep Dive/*
Filter in Web Applications?Normally, when we build web applications that require authentication and authorization mechanisms to serve resources to users, we often use a filter that intercepts all incoming requests to a web application. Such a filter typically uses the URL pattern /*
. Imagine a scenario where your web application serves users with a login form on the default index page. Through this form, we want to enforce authentication before granting access to various resources.
In another scenario, we might want to redirect users to a login page after a session timeout, log all requests to the application, or compress a file before sending it to the client.
In such cases, we prefer a filter over a servlet because of the separation of concerns and centralized control. Servlets are better suited for request handling and content generation, while filters excel at gate-keeping and post-processing. Servlets deal with the core business logic, whereas filters handle cross-cutting concerns such as logging, security, and request or response modification.
Filters can also be chained to process the entire request–response cycle in a modular and layered manner. For all these reasons, filters are a powerful tool in Java web applications—and we often rely on a catch-all filter mapped to the pattern /*.
/*
on Request HandlingLet’s develop a simple web-app to see how a catch-all filter works. I’m using NetBeans IDE 25. You may use your preferred choice of IDE. I’ve also downloaded Apache Tomcat version 11.0.9 and unzipped it at C:\tomcat1109
on my PC.
Using NetBeans I’ve created a web application project and named it InterceptAllApp
. While creating the project I specified the context of the web-app as interceptallapp
. Thus, when we run the newly created web-app, it’ll be available at http://localhost:8080/interceptallapp/
. When we run the web-app we shall see similar output like shown below:
That’s the default index.html
being served. So far so good.
Now, let’s create a few resources as shown in the image below:
First, I’ve created four folders - css
, html
, jsp
, and images
.
Then I created the following files:
style.css
in the css
folderexample.html
in the html
foldersecret.jsp
in the jsp
folderlogo.png
in the images
folder (copied from the Tomcat logo)The css, html,
and images
folders serve static assets. And the jsp
folder has dynamic files - the JSPs.
Let’s now move ahead and create a catch-all filter called InterceptAllFilter
. We’ll display a message indicating the request was blocked by the filter.
public class InterceptAllFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
response.setContentType("text/html");
response.getWriter().println("Your request to "+this.getFullURL(req)+" was blocked by /* Filter");
}
//A helper method to obtain the requested URL
public String getFullURL(HttpServletRequest request) {
String scheme = request.getScheme(); // http or https
String serverName = request.getServerName(); // localhost, domain
int serverPort = request.getServerPort(); // 8080, etc.
String contextPath = request.getContextPath(); // /app
String servletPath = request.getServletPath(); // /login.html
String pathInfo = request.getPathInfo(); // extra path (if any)
String queryString = request.getQueryString(); // ?lang=en
StringBuilder url = new StringBuilder();
url.append(scheme).append("://").append(serverName);
if ((scheme.equals("http") && serverPort != 80) ||
(scheme.equals("https") && serverPort != 443)) {
url.append(":").append(serverPort);
}
url.append(contextPath).append(servletPath);
if (pathInfo != null) {
url.append(pathInfo);
}
if (queryString != null) {
url.append("?").append(queryString);
}
return url.toString();
}
}
The web.xml
:
<filter>
<filter-name>InterceptAllFilter</filter-name>
<filter-class>filters.InterceptAllFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>InterceptAllFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The /*
filter works as expected—it intercepts and blocks all incoming requests, including those for the default index.html
page, unless explicitly allowed.
Let’s allow the default page:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String uri = req.getRequestURI(); // e.g., /webapp/
String ctx = req.getContextPath(); // e.g., /webapp
String path = uri.substring(ctx.length()); // e.g., /
if (path.equals("/")) {
System.out.println("Allowing the URL:" + this.getFullURL(req));
chain.doFilter(request, response);
return;
}
System.out.println("Blocking the URL:" + this.getFullURL(req));
response.setContentType("text/html");
response.getWriter().println("Your request to " + this.getFullURL(req) + " was blocked by /* Filter");
}
It didn’t work because even though we allowed /
, Tomcat didn’t automatically map this to index.html
unless it was explicitly declared in the welcome-file-list
.
We can make it work by declaring the index.html
as a welcome file in the web.xml
as shown below:
<filter>
<filter-name>InterceptAllFilter</filter-name>
<filter-class>filters.InterceptAllFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>InterceptAllFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
This tells Tomcat to serve index.html
when a user visits /interceptallapp/
. And it works now:
But, we see that logo and HTML styles are missing.
/*
Filter?From the Tomcat logs, I see that the filter has allowed /index.html
and blocked everything else:
Allowing the URL: http://localhost:8080/interceptallapp/index.html
Blocking the URL: http://localhost:8080/interceptallapp/css/style.css
Blocking the URL: http://localhost:8080/interceptallapp/images/logo.png
Because the filter catches everything /*, we need to make sure the filter lets static resources like images and stylesheets pass through so that the
DefaultServlet can serve them. Let's modify the
doFilter()` method:
if (path.equals("/")|| path.endsWith(".png") || path.endsWith(".css")) {
System.out.println("Allowing the URL:" + this.getFullURL(req));
chain.doFilter(request, response);
return;
}
And that works, as shown below - logo.png
and style.css
were allowed:
Note that the links to example.html
and secret.jsp
still don’t work. Why? Let’s find out.
So far:
/interceptallapp/*
/
in the doFilter()
method, and then specifying the index.html
as a welcome-file
in web.xml
Now that we’ve seen the problem, let’s look at who is responsible for serving these static files in Tomcat.
Straight from the Tomcat docs: The default servlet is the servlet which serves static resources as well as serves the directory listings (if directory listings are enabled).
The DefaultServlet
can be found declared in C:\tomcat1109\conf\web.xml
. Because I’ve installed Tomcat at C:\tomcat1109
. This servlet is responsible for delivering static resources such as .html, .css, .js,
and images. It does not handle dynamic content like .jsp
.
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>
org.apache.catalina.servlets.DefaultServlet
</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
...
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
We can see that the DefaultServlet
is mapped to the URL pattern /
. Thus, it served the index.html
because the file was declared as a welcome file. Open the web.xml
shown above and modify the debug parameter value from 0 to 1 or 11. Restart the Tomcat server and open Tomcat’s logs (C:\tomcat1109\logs\localhost.2025-07-31.log
) to see the DefaultServlet
serving static content:
31-Jul-2025 19:31:40.085 INFO [http-nio-8080-exec-9] org.apache.catalina.core.ApplicationContext.log default: DefaultServlet.serveResource: Serving resource '/index.html' headers and data
31-Jul-2025 19:31:40.085 INFO [http-nio-8080-exec-9] org.apache.catalina.core.ApplicationContext.log default: DefaultServlet.serveFile: contentType='text/html'
31-Jul-2025 19:31:40.085 INFO [http-nio-8080-exec-9] org.apache.catalina.core.ApplicationContext.log default: DefaultServlet.serveFile: contentLength=891
31-Jul-2025 19:31:40.210 INFO [http-nio-8080-exec-1] org.apache.catalina.core.ApplicationContext.log default: DefaultServlet.serveResource: Serving resource '/css/style.css' headers and data
31-Jul-2025 19:31:40.211 INFO [http-nio-8080-exec-1] org.apache.catalina.core.ApplicationContext.log default: DefaultServlet.serveFile: contentType='text/css'
31-Jul-2025 19:31:40.211 INFO [http-nio-8080-exec-1] org.apache.catalina.core.ApplicationContext.log default: DefaultServlet.serveFile: contentLength=973
31-Jul-2025 19:31:40.218 INFO [http-nio-8080-exec-2] org.apache.catalina.core.ApplicationContext.log default: DefaultServlet.serveResource: Serving resource '/images/logo.png' headers and data
31-Jul-2025 19:31:40.220 INFO [http-nio-8080-exec-2] org.apache.catalina.core.ApplicationContext.log default: DefaultServlet.serveFile: contentType='image/png'
31-Jul-2025 19:31:40.220 INFO [http-nio-8080-exec-2] org.apache.catalina.core.ApplicationContext.log default: DefaultServlet.serveFile: contentLength=8410
31-Jul-2025 19:31:40.245 INFO [http-nio-8080-exec-6] org.apache.catalina.core.ApplicationContext.log default: DefaultServlet.serveResource: Serving resource '/favicon.ico' headers and data
31-Jul-2025 19:31:40.247 INFO [http-nio-8080-exec-6] org.apache.catalina.core.ApplicationContext.log default: DefaultServlet.serveFile: contentType='image/x-icon'
31-Jul-2025 19:31:40.247 INFO [http-nio-8080-exec-6] org.apache.catalina.core.ApplicationContext.log default: DefaultServlet.serveFile: contentLength=21630
Note that DefaultServlet
serves static content only when the filter passes control using chain.doFilter()
.
/*
FilterTo avoid static content being blocked by the filter, we need to explicitly allow static content to pass through.
web.xml
String uri = req.getRequestURI(); // e.g., /interceptallapp/index.html
String ctx = req.getContextPath(); // e.g., /interceptallapp
String path = uri.substring(ctx.length()); // e.g., /index.html
if (path.equals("/") ||
path.equals("/index.html") ||
path.contains("html/") ||
path.endsWith(".css") ||
path.endsWith(".js") ||
path.endsWith(".png") ||
path.endsWith(".jpg") ||
path.endsWith(".ico")) {
chain.doFilter(request, response); // Let DefaultServlet handle it
return;
}
When a filter calls HttpServletResponse.sendRedirect() or otherwise commits the response without invoking chain.doFilter(), the request processing stops at the filter, so the DefaultServlet never gets a chance to serve static resources.
In our example we wanted the JSP file secret.jsp
remain only accessible after a successful login. So, we redirect the user to login.html
page from the filter:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String uri = req.getRequestURI(); // e.g., /webapp/
String ctx = req.getContextPath(); // e.g., /webapp
String path = uri.substring(ctx.length()); // e.g., /
if (path.equals("/")||path.endsWith(".png")||path.endsWith(".css") || path.contains("html/") || path.contains("login.html")) {
System.out.println("Allowing the URL:" + this.getFullURL(req));
chain.doFilter(request, response);
return;
}
System.out.println("Blocking the URL:"+this.getFullURL(req));
if(path.contains("jsp/")) {
res.sendRedirect("../login.html");
return;
}
response.setContentType("text/html");
response.getWriter().println("Your request to " + this.getFullURL(req) + " was blocked by /* Filter");
}
Using a global /* filter gives you centralized control over all incoming requests. However, it requires careful handling to ensure that static resources are not inadvertently blocked. By understanding how Tomcat’s DefaultServlet
works and how to configure welcome files and filter logic properly, you can build secure, robust, and user-friendly Java web applications.