-
Notifications
You must be signed in to change notification settings - Fork 4
Reverse HTTP
This feature was introduced in HAProxy 2.9 and is still EXPERIMENTAL today (HAProxy 3.2).
It enables HAProxy to reverse an HTTP connection: passing from a "server" mode into a "client" mode. To make it simpler, we'll use the following terms in this page:
- The “gateway” is an HAProxy server that is waiting for incoming connections
- The "dialer" is a client who wants to expose an application through the gateway
- The “application” runs behind the dialer. Note: the application may be the dialer itself
The workflow is pretty simple:
- the dialer contacts the gateway and registers itself for the list of applications it wants to expose
- the HTTP connections established between the dialer and the gateway are reversed: they can be used by the gateway to forward requests to the dialer. Basically, the dialer used to be the client, it now becomes the server and the gateway used to be the server, it now becomes the client…
- a client connects to the gateway and wants to browse an application
- the gateway routes the request to the dialer using the "reversed" connection established at step 3
- the dialer can then make a reply (either itself or after having forwarded the request to the relevant application server)
- The gateway forwards the response to the client
As of today, only HAProxy can be used as both the "gateway" and the "dialer", but it's possible to implement the reverse http protocol at the application server level directly. In such a case, a nodejs app or golang one could be a dialer directly.

Let's take an example scenario: we want to expose an application called ABCD. For this, we must configure an haproxy as a Gateway and another one as a dialer.
In order to configure HAProxy as a Gateway, we need:
- A frontend (named GW:registration) used to collect dial-in connections that dialer will use to register themselves (step 1) The registration service is also responsible to associate dialer connections to the relevant application backends (named “GW:” in our case) (step 2)
- A “main” HTTP frontend where clients get connected to to browse the application ABCD (steps 3 to 6)
# service used by DIALERs to register themselves into the GATEWAY
frontend GW:registration
bind 0.0.0.0:444 name https ssl crt ABCD_FQDN_bundle.pem
# dispatch incoming connections into relevant GW services
acl sni_ABCD ssl_fc_sni ABCD_FQDN
tcp-request session attach-srv GW:ABCD/dialers if sni_ABCD
# tunnels to DIALERS which registered for ABCD
backend GW:ABCD
server dialers rhttp@
# endpoint used by CLIENTs to browse ABCD application
frontend main
bind 0.0.0.0:443 name https ssl crt-list ./certs/crtlist
# route traffic for ABCD application to the relevant GW backend
acl app_ABCD req.hdr(host),lower,word(1,:) ABCD_FQDN
use_backend GW:ABCD if app_ABCD
In order to configure HAProxy as a dialer, we need:
- A backend (named dialer:dial-in) to perform the registration with the Gateway (step 1 and 2)
- A frontend (named dialer:dispatch) to route traffic between the Gateway and the relevant application backends (steps 3 to 6)
- A backend to reach application ABCD (steps 3 to 6)
# dispatcher service to route GATEWAY connections to relevant application backend
frontend dialer:dispatch
option socket-stats
bind rhttp@dialer:dial-in/gw nbconn 5 name dial-in
use_backend app_ABCD if { ssl_fc_sni ABCD_FQDN }
# dial in to GATEWAY and registration of exposed applications
backend dialer:dial-in
server gw IP:444 ssl alpn h2 sni str("ABCD_FQDN") # points to GATEWAY’s GW:registration
# standard application backend
backend app_ABCD
http-request set-header x-forwarded-for %[src]
server srv1 IP1:port check
The configuration above quickly presents a single deployment method using SSL traffic between the Gateway and the dialer, but various options exist from the least to the most secured:
- Clear HTTP traffic
- TLS traffic (the one above)
- mTLS
As a last resort, don’t forget you can also implement mTLS on the main frontend on the Gateway to ensure only allowed people can browse the application.
In the configuration of the Gateway, we considered that each backend would be dedicated to one application. It may not be the case in the real world and the gateway may not know in advance what it is going to route. In such a case, we can create a generic backend for dialers and use pool of connections based on common criteria like the SNI or Host header:
- Update the
attach-srvaction in the GW:registration frontend with a "name" setting:tcp-request session attach-srv GW:ABCD/dialers name ssl_fc_sni - And create a generic backend to sort connections based on SNI too:
backend GW:dialers server dialers rhttp@ pool-conn-name ssl_fc_sni
A new feature was introduced in HAProxy 3.2 to ensure all established connections are operational. If not, they’ll be closed and will be replaced by a new one. Just enable the idle-ping directive on relevant bind and server lines of your dialers and gateways.