Howto write apache ProxyPass* rules in HAProxy

Apache mod_proxy

Apache webserver is a widely deployed modular web server.
One of its module is called mod_proxy. It aims to turn the web server into a proxy / reverse proxy server with load-balancing capabilities.

At HAProxy Technologies, we only use HAProxy :). Heh, what else ???
And during some deployments, customers ask us to migrate Apache mod_proxy configuration into HAProxy.
Present article explains how to translate ProxyPass related rules.

ProxyPass, ProxyPassReverse, etc…

Apache mod_proxy defines a few directives which let it forward traffic to a remote server.
They are listed below with a short description.

Note: I know the directives introduced in this article could be used in much complicated ways.

ProxyPass

ProxyPass maps local server URLs to remote servers + URL.
It applies on traffic from client to server.

For example:

ProxyPass /mirror/foo/ http://backend.example.com/

This makes the external URL http://example.com/mirror/foo/bar to be translated and forwarded to a remote server this way: http://backend.example.com/bar

This directive makes apache to update URL and headers to match both external traffic to internal needs.

ProxyPassReverse

ProxyPassReverse Adjusts the URL in HTTP response headers sent from a reverse proxied server. It only updates Location, Content-Location and URL.
It applies to traffic from server to client.

For example:

ProxyPassReverse /mirror/foo/ http://backend.example.com/

This directive makes apache to adapt responses generated by servers following internal urls to match external urls.

ProxyPassReverseCookieDomain

ProxyPassReverseCookieDomain adjusts the Set-Cookie header sent by the server to match external domain name.

It’s usage is pretty simple. For example:

ProxyPassReverseCookieDomain internal-domain public-domain

ProxyPassReverseCookiePath

ProxyPassReverseCookiePath adjusts the Set-Cookie header sent by the server to match external path.

It’s usage is pretty simple. For example:

ProxyPassReverseCookiePath internal-path public-path

Configure ProxyPass and ProxyPassReverse in HAProxy

Bellow, an example HAProxy configuration to make HAProxy work the same way as apache ProxyPass and ProxyPassReverse configuration. It should be added in the backend section while the frontend ensure that only traffic matching this external URL would be redirected to that backend.

frontend ft_global
 acl host_dom.com    req.hdr(Host) dom.com
 acl path_mirror_foo path -m beg   /mirror/foo/
 use_backend bk_myapp if host_dom.com path_mirror_foo

backend bk_myapp
[...]
# external URL                  => internal URL
# http://dom.com/mirror/foo/bar => http://bk.dom.com/bar

 # ProxyPass /mirror/foo/ http://bk.dom.com/bar
 http-request set-header Host bk.dom.com
 reqirep  ^([^ :]*)\ /mirror/foo/(.*)     \1\ /\2

 # ProxyPassReverse /mirror/foo/ http://bk.dom.com/bar
 # Note: we turn the urls into absolute in the mean time
 acl hdr_location res.hdr(Location) -m found
 rspirep ^Location:\ (https?://bk.dom.com(:[0-9]+)?)?(/.*) Location:\ /mirror/foo3 if hdr_location

 # ProxyPassReverseCookieDomain bk.dom.com dom.com
 acl hdr_set_cookie_dom res.hdr(Set-cookie) -m sub Domain= bk.dom.com
 rspirep ^(Set-Cookie:.*)\ Domain=bk.dom.com(.*) \1\ Domain=dom.com\2 if hdr_set_cookie_dom

 # ProxyPassReverseCookieDomain / /mirror/foo/
 acl hdr_set_cookie_path res.hdr(Set-cookie) -m sub Path= 
 rspirep ^(Set-Cookie:.*)\ Path=(.*) \1\ Path=/mirror/foo2 if hdr_set_cookie_path

Notes:
  * http to https redirect rules should be handled by HAProxy itself and not by the application server (to avoid some redirect loops)

Links

7 thoughts on “Howto write apache ProxyPass* rules in HAProxy”

  1. Hi,

    There seem to be a few mistakes in your cookie rewrites:
    res.hdr(Set-cookie) should be res.hdr(Set-Cookie)
    Domain: bk.dom.com should be Domain=bk.dom.com

    Likewise Path: should be Path=

    Otherwise it was very helpful.

    Cheers,
    Pav

  2. Also a lot of cookies do not use capitals in domain and path settings, so the regexes should account for both cases,

    i.e.
    [Dd]omain=
    [Pp]ath=

    Cheers,
    Pav

  3. Hello HAProxy experts!

    My desire is to create the following:

    Interntet -> http://external.com/coolurl -> https://external.com/coolurl -> HAProxy -> http://server.internal.com

    Interntet -> http://external.com/another-coolurl -> https://external.com/another-coolurl -> HAProxy -> http://another-server.internal.com

    I use the configuration bellow and for some reason it works only with first HTML page and no js or image files are delivered.

    Please advice!
    SP

    frontend coolurl-external
    bind :80
    mode http
    option forwardfo
    reqadd X-Forwarded-Proto: http
    redirect scheme https code 301 if !{ ssl_fc }

    frontend coolurl-secure-external
    mode http
    bind :443 ssl crt /etc/haproxy/cert.pem no-tls-tickets ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-RC4-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES128-SHA:AES256-SHA256:AES256-SHA:RC4-SHA

    option forwardfor
    reqadd X-Forwarded-Proto: https

    acl secure dst_port eq 443
    rsprep ^Set-Cookie: (.*) Set-Cookie: 1; Secure if secure
    rspadd Strict-Transport-Security: max-age=31536000 if secure

    acl host_external.com req.hdr(Host) external.com
    acl path_coolurl path -m beg /coolurl
    use_backend coolurl if host_external.com path_coolurl
    acl path another_coolurl path -m beg /another-coolurl
    use_backend another_coolurl if host_external.com path_another_coolurl

    backend coolurl
    # external URL => internal URL
    # http://external.com/coolurl => http://serverv.internal.com
    mode http

    # ProxyPass /coolurl http://server.internal.com
    http-request set-header Host server.internal.com
    reqirep ^([^ :]*) /coolurl(.*) 1 /2

    # ProxyPassReverse /coolurl http://server.internal.com
    # Note: we turn the urls into absolute in the mean time
    acl hdr_location res.hdr(Location) -m found
    reqirep ^Location: (https?://server.internal.com(:[0-9]+)?)?(/.*) Location: /coolurl3 if hdr_location

    # ProxyPassReverseCookieDomain server.internal.com external.com
    acl hdr_set_cookie_dom res.hdr(Set-Cookie) -m sub Domain= server.internal.com
    reqirep ^(Set-Cookie:.*) Domain=server.internal.com(.*) 1 Domain= external.com2 if hdr_set_cookie_dom

    # ProxyPassReverseCookieDomain / /coolurl
    acl hdr_set_cookie_path res.hdr(Set-Cookie) -m sub Path=
    reqirep ^(Set-Cookie:.*) Path= (.*) 1 Path= /coolurl2 if hdr_set_cookie_path

    option httpchk GET /
    server Server-1 10.10.10.10:80 check

    backend another_coolurl
    ….

  4. Hello HAProxy experts!

    My desire is to create the following:

    Interntet -> http://external.com/coolurl -> https://external.com/coolurl -> HAProxy -> http://server.internal.com

    Interntet -> http://external.com/another-coolurl -> https://external.com/another-coolurl -> HAProxy -> http://another-server.internal.com

    I use the configuration bellow and for some reason it works only with the first HTML page and no js or image files are delivered.

    Please advice!
    SP

    frontend coolurl-external
    bind :80
    mode http
    option forwardfo
    reqadd X-Forwarded-Proto: http
    redirect scheme https code 301 if !{ ssl_fc }

    frontend coolurl-secure-external
    mode http
    bind :443 ssl crt /etc/haproxy/cert.pem no-tls-tickets ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-RSA-RC4-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES128-SHA:AES256-SHA256:AES256-SHA:RC4-SHA

    option forwardfor
    reqadd X-Forwarded-Proto: https

    acl secure dst_port eq 443
    rsprep ^Set-Cookie: (.*) Set-Cookie: 1; Secure if secure
    rspadd Strict-Transport-Security: max-age=31536000 if secure

    acl host_external.com req.hdr(Host) external.com
    acl path_coolurl path -m beg /coolurl
    use_backend coolurl if host_external.com path_coolurl
    acl path another_coolurl path -m beg /another-coolurl
    use_backend another_coolurl if host_external.com path_another_coolurl

    backend coolurl
    # external URL => internal URL
    # http://external.com/coolurl => http://serverv.internal.com
    mode http

    # ProxyPass /coolurl http://server.internal.com
    http-request set-header Host server.internal.com
    reqirep ^([^ :]*) /coolurl(.*) 1 /2

    # ProxyPassReverse /coolurl http://server.internal.com
    # Note: we turn the urls into absolute in the mean time
    acl hdr_location res.hdr(Location) -m found
    reqirep ^Location: (https?://server.internal.com(:[0-9]+)?)?(/.*) Location: /coolurl3 if hdr_location

    # ProxyPassReverseCookieDomain server.internal.com external.com
    acl hdr_set_cookie_dom res.hdr(Set-Cookie) -m sub Domain= server.internal.com
    reqirep ^(Set-Cookie:.*) Domain=server.internal.com(.*) 1 Domain= external.com2 if hdr_set_cookie_dom

    # ProxyPassReverseCookieDomain / /coolurl
    acl hdr_set_cookie_path res.hdr(Set-Cookie) -m sub Path=
    reqirep ^(Set-Cookie:.*) Path= (.*) 1 Path= /coolurl2 if hdr_set_cookie_path

    option httpchk GET /
    server Server-1 10.10.10.10:80 check

    backend another_coolurl
    ….

  5. There is either a missing escape in the space, or the space should not be there. I had to escape the space.

    reqirep ^([^ :]*)\ /mirror/foo/(.*) \1\ /\2

    should be

    reqirep ^([^\ :]*)\ /mirror/foo/(.*) \1\ /\2

Leave a Reply

Your email address will not be published. Required fields are marked *