Enable AJP with the Bundled Spring Boot Tomcat Container

Running your Spring Boot apps with the bundled Tomcat container can make your deployments much easier, but only HTTP is enabled by default. Read on to find out how to enable AJP for better performance and stability.

Deploying your Spring Boot app with the bundled Tomcat container is fairly straightforward, saving a lot of time and trouble without having to install and maintain a separate Tomcat server.

By default, the embedded Tomcat server is only configured to listen for HTTP connections. For better performance and stability, you can configure it to listen for AJP connections as well.

To enable AJP, you create a configuration class that returns a TomcatServletWebServerFactory instance with an AJP connector. The following example shows how to configure AJP for a Spring Boot app with a customizable port number:

package com.example.demo;

import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Configuration class for Apache Tomcat AJP (Apache JServ Protocol) connector.
 * This class provides configuration for secure AJP communication with Tomcat.*
 * A basic implementation of this class can be found in TomcatAjpConfigImpl.
 *
 */
@Configuration
public class TomcatAjpConfig {
    
    /**
     * The port number for AJP communication.
     */
    final private int _ajpPort;

    /**
     * Constructs a new TomcatAjpConfig with the specified AJP port.
     *
     * @param ajpPort the port number to be used for AJP communication,
     *                injected from property 'tomcat.ajp.port' in application.properties.
     */
    public TomcatAjpConfig(@Value("${tomcat.ajp.port}") int ajpPort) {
        this._ajpPort = ajpPort;
    }

    /**
     * Gets the port number for the AJP connector.
     *
     * @return The port number for AJP communication
     */
    public Integer getAjpPort() {
          return this._ajpPort;
    }
    
    /**
     * Configures and creates a TomcatServletWebServerFactory with AJP connector settings.
       * The connector is configured with secure HTTPS scheme and specific security parameters.
       *
       * @return Configured TomcatServletWebServerFactory instance with AJP connector
       */
    @Bean
    public TomcatServletWebServerFactory servletContainer() {
          TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
    
          Connector ajpConnector = new Connector("AJP/1.3");
          ajpConnector.setPort(this.getAjpPort());
          ajpConnector.setSecure(true);
          ajpConnector.setScheme("https");
          ajpConnector.setMaxParameterCount(1000);
          ajpConnector.setMaxPostSize(-1);
          ajpConnector.setProperty("secretRequired", "false");
    
          tomcat.addAdditionalTomcatConnectors(ajpConnector);
    
          return tomcat;
    }
}

The port number for AJP communication is configured in the application.properties file. In this example, the port number is set to 9001.

tomcat.ajp.port=9001

Running the App

If you've configured the AJP connector correctly, you should see the following output when you run the app (in this example, the HTTP port is set to the default 8080):

org.apache.coyote.ajp.AjpNioProtocol     : Initializing ProtocolHandler ["ajp-nio-127.0.0.1-9001"]
...
o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with ports 8080 (http), 9001 (https)

Explanation

You first have to annotate the class with @Configuration for Spring to recognize and load it.

@Configuration
public class TomcatAjpConfig {
    ...
}

The constructor of the class takes a parameter for the port number that the AJP connector will be listening on. The value is injected from the application.properties file.

final private int _ajpPort;

public TomcatAjpConfig(@Value("${tomcat.ajp.port}") int ajpPort) {
    this._ajpPort = ajpPort;
}

A servlet container is configured by returning a configured TomcatServletWebServerFactory instance. Make sure to annotate the method with @Bean to make it available to Spring.

@Bean
public TomcatServletWebServerFactory servletContainer() {
      TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();

      Connector ajpConnector = new Connector("AJP/1.3");
      ... config options

      tomcat.addAdditionalTomcatConnectors(ajpConnector);

      return tomcat;
}

The AJP port is configured with the injected value from the application.properties file.

ajpConnector.setPort(this.getAjpPort());

The AJP connector will report to Tomcat that it is using a secure connection through HttpServletRequest.isSecure(). This value does not have any effect on functionality and is for informational purposes only.

ajpConnector.setSecure(true);

The AJP connector will report to Tomcat that it is using the HTTPS scheme through HttpServletRequest.getScheme(). This value does not have any effect on functionality and is for informational purposes only.

ajpConnector.setScheme("https");

The AJP connector will only accept the first 1,000 request parameters (including uploaded files) and ignore any additional parameters. This is to prevent the AJP connector from being overloaded with requests.

ajpConnector.setMaxParameterCount(1000);

The AJP connector will not have a maximum size for POSTed data by setting this value to -1.

ajpConnector.setMaxPostSize(-1);

The AJP connector will not require a secret to be sent with each request.

ajpConnector.setProperty("secretRequired", "false");