mod_proxy_ajp – Apache HTTP Server Version 2.4
This module is used to reverse proxy to a backend application server
(e. g. Apache Tomcat) using the AJP13 protocol. The usage is similar to
an HTTP reverse proxy, but uses the ajp prefix:
Simple Reverse ProxyProxyPass “/app” “ajp”
Options such as the secret option of Tomcat (required by
default since Tomcat 8. 5. 51 and 9. 0. 31) can just be added as a separate
parameter at the end of ProxyPass
or BalancerMember. This parameter
is available in Apache HTTP Server 2. 4. 42 and later:
Simple Reverse Proxy with secret optionProxyPass “/app” “ajp” secret=YOUR_AJP_SECRET
Balancers may also be used:
Balancer Reverse Proxy
BalancerMember “ajp” loadfactor=1
BalancerMember “ajp” loadfactor=2
ProxyPass “/app” “balancercluster/app”
Note that usually no
directive is necessary. The AJP request includes the original host
header given to the proxy, and the application server can be expected
to generate self-referential headers relative to this host, so no
rewriting is necessary.
The main exception is when the URL path on the proxy differs from that
backend. In this case, a redirect header can be rewritten relative to the
original host URL (not the backend ajp URL), for
Rewriting Proxied PathProxyPass “/apps/foo” “ajp”
ProxyPassReverse “/apps/foo” ”
However, it is usually better to deploy the application on the backend
server at the same path as the proxy rather than to take this approach.
Environment variables whose names have the prefix AJP_
are forwarded to the origin server as AJP request attributes
(with the AJP_ prefix removed from the name of the key).
Overview of the protocol
The AJP13 protocol is packet-oriented. A binary format
was presumably chosen over the more readable plain text for reasons of
performance. The web server communicates with the servlet container over
TCP connections. To cut down on the expensive process of socket creation,
the web server will attempt to maintain persistent TCP connections to the
servlet container, and to reuse a connection for multiple request/response
Once a connection is assigned to a particular request, it will not be
used for any others until the request-handling cycle has terminated. In
other words, requests are not multiplexed over connections. This makes
for much simpler code at either end of the connection, although it does
cause more connections to be open at once.
Once the web server has opened a connection to the servlet container,
the connection can be in one of the following states:
Idle No request is being handled over this connection.
Assigned The connection is handling a specific request.
Once a connection is assigned to handle a particular request, the basic
request information (e. HTTP headers, etc) is sent over the connection in
a highly condensed form (e. common strings are encoded as integers).
Details of that format are below in Request Packet Structure. If there is a
body to the request (content-length > 0), that is sent in a
separate packet immediately after.
At this point, the servlet container is presumably ready to start
processing the request. As it does so, it can send the
following messages back to the web server:
SEND_HEADERS Send a set of headers back to the browser.
SEND_BODY_CHUNK Send a chunk of body data back to the browser.
GET_BODY_CHUNK Get further data from the request if it hasn’t all
been transferred yet. This is necessary because the packets have a fixed
maximum size and arbitrary amounts of data can be included the body of a
request (for uploaded files, for example). (Note: this is unrelated to
HTTP chunked transfer).
END_RESPONSE Finish the request-handling cycle.
Each message is accompanied by a differently formatted packet of data.
See Response Packet Structures below for details.
Basic Packet Structure
There is a bit of an XDR heritage to this protocol, but it differs
in lots of ways (no 4 byte alignment, for example).
AJP13 uses network byte order for all data types.
There are four data types in the protocol: bytes, booleans,
integers and strings.
ByteA single byte.
A single byte, 1 = true, 0 = false.
Using other non-zero values as true (i. e. C-style) may work in some places,
but it won’t in others.
A number in the range of 0 to 2^16 (32768). Stored in
2 bytes with the high-order byte first.
A variable-sized string (length bounded by 2^16). Encoded with
the length packed into two bytes first, followed by the string
(including the terminating ‘