I've been trying to configure Apache web server with a Spring Boot app that uses embedded Tomcat. Before Spring Boot I used to create an ajp.conf file like:
<VirtualHost *:80>
ServerName localhost
<Proxy *>
AddDefaultCharset Off
Order deny,allow
Allow from all
</Proxy>
ProxyPass /app ajp://localhost:8009/app
ProxyPassReverse /app ajp://localhost:8009/app
</VirtualHost>
And include in the httpd.conf file like
Include /opt/lampp/apache2/conf/ajp.conf
And in the Tomcat's server.xml file, I used to configure it to listen to port 8009
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" connectionTimeout="5000"
This setup works. But, now with Spring Boot I am trying to achieve something similar with an embedded tomcat. I read Spring Boot Documentation here and added the following propertied on my application.yml file:
server:
port: 8080
tomcat:
remote_ip_header: x-forwarded-for
protocol_header: x-forwarded-proto
My ajp.conf file looks like so:
<VirtualHost *:80>
ServerName localhost
<Proxy *>
AddDefaultCharset Off
Order deny,allow
Allow from all
</Proxy>
ProxyPass /app ajp://localhost:8009/
ProxyPassReverse /app ajp://localhost:8009/
</VirtualHost>
I have my spring boot tomcat configuration class as
@Configuration
public class TomcatConfiguration {
private final Logger log = LoggerFactory.getLogger(TomcatConfiguration.class);
@Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
tomcat.addAdditionalTomcatConnectors(createConnector());
tomcat.addContextValves(createRemoteIpValves());
return tomcat;
}
private RemoteIpValve createRemoteIpValves(){
RemoteIpValve remoteIpValve = new RemoteIpValve();
remoteIpValve.setRemoteIpHeader("x-forwarded-for");
remoteIpValve.setProtocolHeader("x-forwarded-protocol");
return remoteIpValve;
}
private Connector createConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
connector.setScheme("ajp");
connector.setProtocol("AJP/1.3");
connector.setRedirectPort(8443);
//connector.setSecure(true);
connector.setPort(8009);
return connector;
}
On my apache error logs I see:
AH01080: ajp_msg_check_header() got bad signature 4854
[proxy_ajp:error] [pid 24073] AH01031: ajp_ilink_receive() received bad header
[proxy_ajp:error] ajp_read_header: ajp_ilink_receive failed
[proxy_ajp:error] (120007)APR does not understand this error code: [client xx.xx.xx.xx:60916] AH00878: read response failed from (null) (*)
Not sure what's going on here. I searched a lot online, but could not find a good documentation on how to serve tomcat behind apache with spring boot apps. Eventually, I would like to load balance multiple tomcat instances too.
Deduced from the comments above:
@Configuration
public class TomcatAjpConfig {
@Bean
@SuppressWarnings("static-method")
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
tomcat.addAdditionalTomcatConnectors(createConnector());
tomcat.addContextValves(createRemoteIpValves());
return tomcat;
}
private static RemoteIpValve createRemoteIpValves() {
RemoteIpValve remoteIpValve = new RemoteIpValve();
remoteIpValve.setRemoteIpHeader("x-forwarded-for");
remoteIpValve.setProtocolHeader("x-forwarded-proto");
return remoteIpValve;
}
private static Connector createConnector() {
Connector connector = new Connector("AJP/1.3");
connector.setPort(8009);
return connector;
}
}
Had a similar problem but with HTTP-Proxy. After some debugging of Spring Boot 1.3 I found the following solution. It should be similar for the AJP Proxy.
1. You have to setup the headers on your Apache proxy:
<VirtualHost *:443>
ServerName www.myapp.org
ProxyPass / http://127.0.0.1:8080/
RequestHeader set X-Forwarded-Proto https
RequestHeader set X-Forwarded-Port 443
ProxyPreserveHost On
... (SSL directives omitted for readability)
</VirtualHost>
2. You have to tell your Spring Boot app to use these headers. So put the following line in your application.properties (or any other place where Spring Boots understands properties):
server.use-forward-headers=true
If you do these two things correctly, every redirect your application sends will not go to http://127.0.0.1:8080/[path] but automatically to https://www.myapp.com/[path]
Update 1. The documentation about this topic is here. You should read it at least to be aware of the property server.tomcat.internal-proxies
which defines the range of IP-addresses for proxy servers that can be trusted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With