Set CORS header in Tomcat

If it's a static site, then starting with Tomcat 7.0.41, you can easily control CORS behavior via a built-in filter.

References:

  • Tomcat 7
  • Tomcat 9

Pretty much the only thing you have to do is edit the global web.xml in CATALINA_HOME/conf and add the filter definition:

     <!-- ================== Built In Filter Definitions ===================== -->

      ...

     <filter>
       <filter-name>CorsFilter</filter-name>
       <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
     </filter>
     <filter-mapping>
       <filter-name>CorsFilter</filter-name>
       <url-pattern>/*</url-pattern>
     </filter-mapping>

    <!-- ==================== Built In Filter Mappings ====================== -->

Be aware, though, that Firefox does not like Access-Control-Allow-Origin: * and requests with credentials (cookies): when responding to a credentialed request, server must specify a domain, and cannot use wild carding.

If you want to debugs requests in this situation, please be aware that CORS headers are only sent if there is a cross-origin request according to this flow-chart. CORS flow chart

(tomcat.apache.org/tomcat-8.0-doc/images/cors-flowchart.png)


Here is a very basic filter that will add the CORS headers. Note that by default, this will enable all domains and methods so you should customize it to fit your needs.

It also needs to be the first filter in your web.xml.

package com.conductiv.api.listener;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CORSFilter implements Filter {
    public void destroy() {
    }
    public static String VALID_METHODS = "DELETE, HEAD, GET, OPTIONS, POST, PUT";

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest httpReq = (HttpServletRequest) req;
        HttpServletResponse httpResp = (HttpServletResponse) resp;

        // No Origin header present means this is not a cross-domain request
        String origin = httpReq.getHeader("Origin");
         if (origin == null) {
            // Return standard response if OPTIONS request w/o Origin header
           if ("OPTIONS".equalsIgnoreCase(httpReq.getMethod())) {
                httpResp.setHeader("Allow", VALID_METHODS);
                httpResp.setStatus(200);
                return;
            }
        } else {
            // This is a cross-domain request, add headers allowing access
            httpResp.setHeader("Access-Control-Allow-Origin", origin);
            httpResp.setHeader("Access-Control-Allow-Methods", VALID_METHODS);

            String headers = httpReq.getHeader("Access-Control-Request-Headers");
            if (headers != null)
                httpResp.setHeader("Access-Control-Allow-Headers", headers);

            // Allow caching cross-domain permission
            httpResp.setHeader("Access-Control-Max-Age", "3600");
        }
        // Pass request down the chain, except for OPTIONS
        if (!"OPTIONS".equalsIgnoreCase(httpReq.getMethod())) {
            chain.doFilter(req, resp);
        }
 }

    public void init(FilterConfig config) throws ServletException {

    }

}

You need to add a Filter to add the additional header and configure it to all paths

<filter>
  <filter-name>header</filter-name>
  <filter-class>...</filter-class>
</filter>
<filter-mapping>
  <filter-name>header</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Please note, that to configure CORS filter with value *, except enabling the filer you also need to add configuration for parameter <param-name>cors.allowed.origins</param-name> as follows (building on top of Johannes Jander answer):

 <!-- ================== Built In Filter Definitions ===================== -->

  ...

 <filter>
   <filter-name>CorsFilter</filter-name>
   <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
   <init-param>
     <param-name>cors.allowed.origins</param-name>
     <param-value>*</param-value>
   </init-param>
 </filter>
 <filter-mapping>
   <filter-name>CorsFilter</filter-name>
   <url-pattern>/*</url-pattern>
 </filter-mapping>

<!-- ==================== Built In Filter Mappings ====================== -->