This demo indicates how to configure the reverse proxy to backend application(context path is exists)
on Tomcat using AJP connector for specific path(e.g. /hello
).
/usr/local/apache2/htdocs (Document root for 'http://localhost:10080/')
├── index.html
├── error
│ └── 500.html
└── hello (application path for spring-boot) [1]
[1] Does not exists directory and files. This resource will response by tha Tomcat(Spring Boot application) using AJP connector.
- Installed the JDK 8+
- Installed the Docker(Docker Compose)
- Internet access
「Apache(10080:80)」 <-----(AJP)-----> 「Tomcat(8009)[2]」
[2] Can access 18080 port using HTTP protocol
- Version: 8.5 (Spring Boot 2.0)
- Local Port : AJP:8009,HTTP:8080(default)
- External Port for docker container : AJP:NONE, HTTP:18080
- Context Path : /app
@SpringBootApplication
public class ApacheSpringBootDemoApplication {
// ...
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
Connector connector = new Connector("org.apache.coyote.ajp.AjpNioProtocol");
connector.setAttribute("maxThreads", 100);
connector.setPort(8009);
connector.setRedirectPort(8043);
connector.setURIEncoding("UTF-8");
factory.addAdditionalTomcatConnectors(connector);
return factory;
}
// ...
}
src/main/resources/application.properties
server.servlet.context-path=/app
@SpringBootApplication
public class ApacheSpringBootDemoApplication {
// ...
@Controller
@RequestMapping("/hello")
static class Hello {
@GetMapping
String hello(RedirectAttributes attributes, @RequestParam(required = false) String word) {
attributes.addFlashAttribute("text", word);
attributes.addAttribute("t", System.currentTimeMillis());
return "redirect:/hello/foo";
}
@GetMapping("{name}")
@ResponseBody
String hello(@PathVariable String name, @ModelAttribute("text") String text) {
return "hello " + name + " with " + text + " !";
}
@GetMapping("root")
String root() {
return "redirect:/";
}
@GetMapping("help")
String help() {
return "redirect:/help/help.html";
}
@GetMapping("error")
String error() {
return "redirect:/error/500.html";
}
}
}
- Version: 2.4
- Local Port : 80 (default)
- External Port for docker container : 10080
The /hello
or sub resources send to ajp://ap:8009/app/hello
using reverse proxy.
apache/httpd.conf(/usr/local/apache2/conf/httpd.conf)
ProxyRequests Off
ProxyPass /hello ajp://ap:8009/app/hello
ProxyPassReverse / /app/
ProxyPassReverseCookiePath /app /
$ ./mvnw clean package
$ docker-compose up --build
Building ap
Step 1/5 : FROM java:8
---> d23bdf5b1b1b
Step 2/5 : ADD target/apache-spring-boot-demo-0.0.1-SNAPSHOT.jar /opt/spring/apache-spring-boot-demo.jar
---> 1ecdb08692f4
Step 3/5 : EXPOSE 8080
---> Running in daa51ff86cca
Removing intermediate container daa51ff86cca
---> 34afd4d2d72c
Step 4/5 : WORKDIR /opt/spring/
---> Running in 9b31153d1117
Removing intermediate container 9b31153d1117
---> 830ddc97f3bf
Step 5/5 : ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/urandom", "-jar", "apache-spring-boot-demo.jar"]
---> Running in 09d6125c264d
Removing intermediate container 09d6125c264d
---> 55803c20b68b
Successfully built 55803c20b68b
Successfully tagged apache-spring-boot-demo_ap:latest
Building apache-httpd
Step 1/3 : FROM httpd:2.4
---> d595a4011ae3
Step 2/3 : COPY ./httpd.conf /usr/local/apache2/conf/httpd.conf
---> 7ab73c0c1d24
Step 3/3 : COPY ./htdocs /usr/local/apache2/htdocs
---> 6f3262176a2a
Successfully built 6f3262176a2a
Successfully tagged apache-spring-boot-demo_apache-httpd:latest
Recreating apache-spring-boot-demo_ap_1 ... done
Recreating apache-spring-boot-demo_apache-httpd_1 ... done
Attaching to apache-spring-boot-demo_ap_1, apache-spring-boot-demo_apache-httpd_1
apache-httpd_1 | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.3. Set the 'ServerName' directive globally to suppress this message
apache-httpd_1 | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.3. Set the 'ServerName' directive globally to suppress this message
apache-httpd_1 | [Mon Sep 17 11:02:23.297152 2018] [mpm_event:notice] [pid 1:tid 140224708573056] AH00489: Apache/2.4.34 (Unix) configured -- resuming normal operations
apache-httpd_1 | [Mon Sep 17 11:02:23.297326 2018] [core:notice] [pid 1:tid 140224708573056] AH00094: Command line: 'httpd -D FOREGROUND'
ap_1 |
ap_1 | . ____ _ __ _ _
ap_1 | /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
ap_1 | ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
ap_1 | \\/ ___)| |_)| | | | | || (_| | ) ) ) )
ap_1 | ' |____| .__|_| |_|_| |_\__, | / / / /
ap_1 | =========|_|==============|___/=/_/_/_/
ap_1 | :: Spring Boot :: (v2.0.5.RELEASE)
ap_1 |
ap_1 | 2018-09-17 11:02:23.600 INFO 1 --- [ main] c.e.a.ApacheSpringBootDemoApplication : Starting ApacheSpringBootDemoApplication v0.0.1-SNAPSHOT on 1d360873f4a1 with PID 1 (/opt/spring/apache-spring-boot-demo.jar started by root in /opt/spring)
ap_1 | 2018-09-17 11:02:23.604 INFO 1 --- [ main] c.e.a.ApacheSpringBootDemoApplication : No active profile set, falling back to default profiles: default
ap_1 | 2018-09-17 11:02:23.681 INFO 1 --- [ main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6aceb1a5: startup date [Mon Sep 17 11:02:23 UTC 2018]; root of context hierarchy
ap_1 | 2018-09-17 11:02:25.721 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8081 (http) 8009 (http)
ap_1 | 2018-09-17 11:02:25.765 INFO 1 --- [ main] org.apache.coyote.ajp.AjpNioProtocol : Initializing ProtocolHandler ["ajp-nio-8009"]
ap_1 | 2018-09-17 11:02:25.778 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
ap_1 | 2018-09-17 11:02:25.779 INFO 1 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.34
ap_1 | 2018-09-17 11:02:25.803 INFO 1 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/usr/java/packages/lib/amd64:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib]
ap_1 | 2018-09-17 11:02:25.932 INFO 1 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/app] : Initializing Spring embedded WebApplicationContext
ap_1 | 2018-09-17 11:02:25.933 INFO 1 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2255 ms
ap_1 | 2018-09-17 11:02:26.026 INFO 1 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Servlet dispatcherServlet mapped to [/]
ap_1 | 2018-09-17 11:02:26.031 INFO 1 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
ap_1 | 2018-09-17 11:02:26.032 INFO 1 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
ap_1 | 2018-09-17 11:02:26.033 INFO 1 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
ap_1 | 2018-09-17 11:02:26.033 INFO 1 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
ap_1 | 2018-09-17 11:02:26.233 INFO 1 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
ap_1 | 2018-09-17 11:02:26.584 INFO 1 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6aceb1a5: startup date [Mon Sep 17 11:02:23 UTC 2018]; root of context hierarchy
ap_1 | 2018-09-17 11:02:26.672 INFO 1 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/hello/{name}],methods=[GET]}" onto java.lang.String com.example.apachespringbootdemo.ApacheSpringBootDemoApplication$Hello.hello(java.lang.String)
ap_1 | 2018-09-17 11:02:26.674 INFO 1 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/hello],methods=[GET]}" onto java.lang.String com.example.apachespringbootdemo.ApacheSpringBootDemoApplication$Hello.hello()
ap_1 | 2018-09-17 11:02:26.675 INFO 1 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/hello/root],methods=[GET]}" onto java.lang.String com.example.apachespringbootdemo.ApacheSpringBootDemoApplication$Hello.root()
ap_1 | 2018-09-17 11:02:26.676 INFO 1 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/hello/error],methods=[GET]}" onto java.lang.String com.example.apachespringbootdemo.ApacheSpringBootDemoApplication$Hello.error()
ap_1 | 2018-09-17 11:02:26.676 INFO 1 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/hello/help],methods=[GET]}" onto java.lang.String com.example.apachespringbootdemo.ApacheSpringBootDemoApplication$Hello.help()
ap_1 | 2018-09-17 11:02:26.681 INFO 1 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
ap_1 | 2018-09-17 11:02:26.682 INFO 1 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
ap_1 | 2018-09-17 11:02:26.721 INFO 1 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
ap_1 | 2018-09-17 11:02:26.722 INFO 1 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
ap_1 | 2018-09-17 11:02:26.778 WARN 1 --- [ main] ion$DefaultTemplateResolverConfiguration : Cannot find template location: classpath:/templates/ (please add some templates or check your Thymeleaf configuration)
ap_1 | 2018-09-17 11:02:26.954 INFO 1 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
ap_1 | 2018-09-17 11:02:26.994 INFO 1 --- [ main] org.apache.coyote.ajp.AjpNioProtocol : Starting ProtocolHandler ["ajp-nio-8009"]
ap_1 | 2018-09-17 11:02:27.003 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8081 (http) 8009 (http) with context path '/app'
ap_1 | 2018-09-17 11:02:27.008 INFO 1 --- [ main] c.e.a.ApacheSpringBootDemoApplication : Started ApacheSpringBootDemoApplication in 3.913 seconds (JVM running for 4.489)
This access is confirmed basic settings for apache http server.
$ curl -v -s -b -L http://localhost:10080/
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 10080 (#0)
> GET / HTTP/1.1
> Host: localhost:10080
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 17 Sep 2018 15:02:33 GMT
< Server: Apache/2.4.34 (Unix)
< Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
< ETag: "2d-432a5e4a73a80"
< Accept-Ranges: bytes
< Content-Length: 45
< Content-Type: text/html
<
<html><body><h1>It works!</h1></body></html>
* Connection #0 to host localhost left intact
Traffic sequences are:
- Client ----> Apache ----> Client
This access is confirmed settings for proxy.
$ curl -v -b -s -L http://localhost:10080/hello?word=123
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 10080 (#0)
> GET /hello?word=123 HTTP/1.1
> Host: localhost:10080
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 302 302
< Date: Mon, 17 Sep 2018 15:04:46 GMT
< Server: Apache/2.4.34 (Unix)
* Added cookie JSESSIONID="C7C63F83C18B97F3A8EB308208DD80D1" for domain localhost, path /, expire 0
< Set-Cookie: JSESSIONID=C7C63F83C18B97F3A8EB308208DD80D1; Path=/; HttpOnly
< Location: http://localhost:10080/hello/foo?t=1537196686967
< Content-Language: en
< Content-Length: 0
<
* Connection #0 to host localhost left intact
* Issue another request to this URL: 'http://localhost:10080/hello/foo?t=1537196686967'
* Found bundle for host localhost: 0x7ffb76424bd0 [can pipeline]
* Re-using existing connection! (#0) with host localhost
* Connected to localhost (::1) port 10080 (#0)
> GET /hello/foo?t=1537196686967 HTTP/1.1
> Host: localhost:10080
> User-Agent: curl/7.54.0
> Accept: */*
> Cookie: JSESSIONID=C7C63F83C18B97F3A8EB308208DD80D1
>
< HTTP/1.1 200 200
< Date: Mon, 17 Sep 2018 15:04:46 GMT
< Server: Apache/2.4.34 (Unix)
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 20
<
* Connection #0 to host localhost left intact
hello foo with 123 !
Traffic sequences are:
- Client ----> Apache --(Proxy)--> Tomcat ----> Apache ----> Client
- Client --(Redirect)--> Apache --(Proxy)--> Tomcat ----> Apache ----> Client
This access is confirmed settings for reverse proxy.
$ curl -v -b -s -L http://localhost:10080/hello/error
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 10080 (#0)
> GET /hello/error HTTP/1.1
> Host: localhost:10080
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 302 302
< Date: Mon, 17 Sep 2018 15:05:43 GMT
< Server: Apache/2.4.34 (Unix)
< Location: http://localhost:10080/error/500.html
< Content-Language: en
< Content-Length: 0
<
* Connection #0 to host localhost left intact
* Issue another request to this URL: 'http://localhost:10080/error/500.html'
* Found bundle for host localhost: 0x7fb2ce424bd0 [can pipeline]
* Re-using existing connection! (#0) with host localhost
* Connected to localhost (::1) port 10080 (#0)
> GET /error/500.html HTTP/1.1
> Host: localhost:10080
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 17 Sep 2018 15:05:43 GMT
< Server: Apache/2.4.34 (Unix)
< Last-Modified: Mon, 17 Sep 2018 10:49:59 GMT
< ETag: "5a-5760eef3e23c0"
< Accept-Ranges: bytes
< Content-Length: 90
< Content-Type: text/html
<
<html>
<head>
<title>server error</title>
</head>
<body>
Server Error.
</body>
* Connection #0 to host localhost left intact
</html>
Traffic sequences are:
- Client ----> Apache --(Proxy)--> Tomcat ----> Apache ----> Client
- Client --(Redirect)--> Apache ----> Client