Rewriting HTTP Protocol

Rewriting HTTP protocol is a technique that allows HAProxy to change content dynamically while data is exchanged between a client and a server. It is often used to maintain compatibility between old and new URLs or to turn user-friendly URLs into CMS-friendly URLs, etc.

In HAProxy, rewriting HTTP requests or responses depends on two types of configuration directives:

  • HTTP rules: http-request and http-response (recommended)
  • Legacy rules (still useful): reqrep / reqirep and rsprep / rspirep
Beware that HTTP rewriting may have side impacts on Web applications.

Rewriting HTTP Requests

In this mode, HAProxy rewrites requests sent from clients before it forwards them to the server.

Set the request method in an HTTP header

HAProxy can change the HTTP method of the request from the client using the following directive:

http-request set-method <fmt> [<condition>]

This directive expects the following parameters:

<fmt> a log format variable
<condition> (optional) a condition to apply this rule

Examples:

1. Change GET to POST on the /login url of the website:

acl url_login  path_beg -i /login
http-request set-method POST if METH_GET url_login

2. Turn a GET method into a POST if there is data in the body of the request:

acl request_data req.hdr_val(Content-Length) gt 0
http-request set-method POST if METH_GET request_data

Add a header to an HTTP request

HAProxy can add a Header field to the request from the client using the following directive:

http-request add-header <name> <fmt> [<condition>]

This directive expects the following parameters:

<name> the name of the header to create
<fmt> a log format variable
<condition> (optional) a condition to apply this rule

Examples:

1. Add a X-Forwarded-For containing the client IP address:

http-request add-header X-Forwarded-For %[src]

2. Add a X-Forwarded-For containing the client IP address if none were already present:

acl h_xff_exists req.hdr(X-Forwarded-For) -m found
http-request add-header X-Forwarded-For %[src] unless h_xff_exists

Set a header in the HTTP request

HAProxy can set a Header field in the request from the client using the following directive:

http-request set-header <name> <fmt> [<condition>]
set-header removes first any header in existing field that matches <name>.

This directive expects the following parameters:

<name> the name of the header to set
<fmt> a log format variable
<condition> (optional) a condition to apply this rule

Examples:

1. Erase any existing X-Forwarded-For and creates a new one containing the client IP address:

http-request set-header X-Forwarded-For %[src]

2. Change the Host header field when traffic is redirected to the maintenance back end:

backend maintenance
http-request set-header Host maintenance.domain.com

Delete a header in the HTTP request

HAProxy can delete a Header field from the request from the client using the following directive:

http-request del-header <name> [<condition>]

This directive expects the following parameters:

<name> the name of the header to delete
<condition> (optional) a condition to apply this rule

Examples:

1. Delete any existing X-Forwarded-For header field

http-request del-header X-Forwarded-For

2. Delete all cookies before sending a request to the cache (Varnish / nginx) servers:

backend b_caches
acl at_least_one_cookie req.cook_cnt() gt 0
http-request del-header Cookie if at_least_one_cookie

Replace all values in an HTTP header field

Note: HTTP RFC specifies that the comma ‘,’ character is a Header field delimiter. It means that:

X-Forwarded-For: 192.168.0.1, 10.0.0.1

is equivalent to:

X-Forwarded-For: 192.168.0.1
X-Forwarded-For: 10.0.0.1

HAProxy can update a Header field value while taking into account the entire line, regardless of commas:

http-request replace-header <name> <match-regex> <replace-fmt> [<condition>]

This directive expects the following parameters:

<name> the name of the header to update
<match-regex> a regex to match the content to update
<replace-fmt> a log format variable for the new value
<condition> (optional) a condition to apply this rule

Examples:

To update an X-Forwarded-For header to add the client IP at the top of the list:

acl h_xff_exists req.hdr(X-Forwarded-For) -m found
http-request replace-header X-Forwarded-For (.*) %[src],1 if h_xff_exists

The example above updates the string below:

X-Forwarded-For: 192.168.100.1, 10.0.0.2

to become the following one (considering the client IP is 172.16.0.2):

X-Forwarded-For: 172.16.0.2, 192.168.100.1, 10.0.0.2

Replace a given value of an HTTP header field

Note: HTTP RFC specifies that the comma ‘,’ character is a Header field delimiter. It means that

X-Forwarded-For: 192.168.0.1, 10.0.0.1

is equivalent to:

X-Forwarded-For: 192.168.0.1
X-Forwarded-For: 10.0.0.1

HAProxy can update a Header field value while taking into account the comma character as a separator. It means that the update will apply to each individual value.

http-request replace-value <name> <match-regex> <replace-fmt> [<condition>]

This directive expects the following parameters:

<name> the name of the header to update
<match-regex> a regex to match the content to update
<replace-fmt> a log format variable for the new value
<condition> (optional) a condition to apply this rule

Examples:

To update each Host header to remove the colon and port, if any:

http-request replace-value Host (.*):.* 1

The example above updates the string below:

Host: www.domain.com:80, static.domain.com:8080

into the following one:

Host: www.domain.com, static.domain.com

Rewriting HTTP URLs

URL rewriting is the technique used to “translate” a cryptic URL into a user-friendly one that the server can understand.

Set the URL path

HAProxy can rewrite the path of the HTTP request:

http-request set-path <fmt> [<condition>]

This directive expects the following parameters:

<fmt> a log format variable
<condition> (optional) a condition to apply this rule

Examples:

To change the URL path for JPG images from the /images/ directory on the server, only if not already set:

acl p_ext_jpg path_end -i .jpg
acl p_folder_images path_beg -i /images/
http-request set-path /images/%[path] if !p_folder_images p_ext_jpg

The example above changes the requests below:

GET /images/flower.jpg HTTP/1.1
GET /daisy.jpg HTTP/1.1

into the following ones, respectively:

GET /images/flower.jpg HTTP/1.1
GET /images/daisy.jpg HTTP/1.1

Set the query string

HAProxy can rewrite the query string of the HTTP request:

http-request set-query <fmt> [<condition>]

This directive expects the following parameters:

<fmt> a log format variable
<condition> (optional) a condition to apply this rule

Examples:

1. Replace %3D with “=” in the query string:

http-request set-query %[query,regsub(%3D,=,g)]

2. Reorder the parameters in the query string to ensure the user parameter comes before the group parameter:

http-request set-query user=%[urlp(user)]&group=%[urlp(group)]

The example above changes the request below:

GET /test.php?group=admin&user=foo HTTP/1.1

into the following one:

GET /test.php?user=foo&group=admin HTTP/1.1

Set the URI

HAProxy can rewrite the entire URI string of the HTTP request. This includes HTTP scheme, authority, path, and query string:

http-request set-uri <fmt> [<condition>]

This directive expects the following parameters:

<fmt> a log format variable
<condition> (optional) a condition to apply this rule

Examples:

To create a full URL when forwarding traffic to a proxy server:

backend b_squid
	acl https ssl_fc
	http-request set-uri https://%[req.hdr(Host)]%[path]?%[query] if https
	http-request set-uri http://%[req.hdr(Host)]%[path]?%[query] unless https

The example above changes the request below:

GET /test.php?group=admin&user=foo HTTP/1.1
	Host: www.domain.com

into the following one if the user got connected over HTTP on HAProxy:

GET http://www.domain.com/test.php?group=admin&user=foo HTTP/1.1

or into the following one if the user got connected over HTTPs on HAProxy:

GET https://www.domain.com/test.php?group=admin&user=foo HTTP/1.1
In this case, you should turn GET into CONNECT as well.

Rewrite Anywhere in the Request Using Regexes

The legacy statements reqrep and reqirep are still useful in cases not yet covered by http-request rules.

The statement reqrep applies a regex to each line of the request buffer in a case sensitive manner. As does reqirep, which is case insensitive:

reqrep  <search> <replace> [<cond>] reqirep <search> <replace> [<cond>]

This directive expects the following parameters:

<search> a regular expression to search the content to replace
<replace> a regular expression to apply the modification
[<condition>] (optional) a condition to apply this rule
HAProxy uses PCRE compatible regular expressions. For more information about PCRE syntax, see Regex Quick Start and Regex Cheat Sheet.

Examples:

1. Replace jpg folder with images in the HTTP URL path:

reqrep ^([^ ]*) /jpg/(.*)     1 /images/2

2. Rewrite the Host header from static.domain.com to www.domain.com

acl h_static hdr(Host) -m beg static.domain.com
reqirep ^Host: static.domain.com      Host: www.domain.com if h_static

Rewriting HTTP responses

In this mode, HAProxy rewrites responses sent from the server before it forwards them to the client.

Add a header in the response

HAProxy can add a Header field to the response from the server using the following directive:

http-response add-header <name> <fmt> [<condition>]

This directive expects the following parameters:

<name> the name of the header to create
<fmt> a log format variable
<condition> (optional) a condition to apply this rule

Examples:

1. Add a header field X-Via containing the client name of the current HAProxy server processing the traffic:

http-response add-header X-Via %[env(HOSTNAME)]

2. Add a header field containing the back end and server name that processed the response:

http-response add-header X-App-Server %b/%s

This will insert the following header:

X-App-Server: b_app/appsrv1

Set a header in the response

HAProxy can set a Header field in the response from the server using the following directive:

http-response set-header <name> <fmt> [<condition>]
set-header removes first any existing header field matching <name>

This directive expects the following parameters:

<name> the name of the header to set
<fmt> a log format variable
<condition> (optional) a condition to apply this rule

Examples:

To hide the header Server set by the server:

http-response set-header Server webserver

Delete a header in the response

HAProxy can delete a Header field from the response sent by the server using the following directive:

http-response del-header <name> [<condition>]

This directive expects the following parameters:

<name> the name of the header to delete
<condition> (optional) a condition to apply this rule

Examples:

To delete certain headers set by Varnish:

backend b_static
	http-response del-header X-Varnish
	http-response del-header X-Varnish-Cache
	http-response del-header X-Varnish-Server
	http-response del-header X-Cache

Replace all values of a header field

Note: HTTP RFC specifies that the comma ‘,’ character is a Header field delimiter. It means that

X-Forwarded-For: 192.168.0.1, 10.0.0.1

is equivalent to:

X-Forwarded-For: 192.168.0.1
X-Forwarded-For: 10.0.0.1

HAProxy can update a Header field value while taking into account the whole line, regardless of commas:

http-response replace-header <name> <match-regex> <replace-fmt> [<condition>]

This directive expects the following parameters:

<name> the name of the header to update
<match-regex> a regex to match the content to update
<replace-fmt> a log format variable for the new value
<condition> (optional) a condition to apply this rule

Examples:

To update the cookie JSESSIONID set by the server with the Secure flag if the client side connection is ciphered:

acl https          ssl_fc
acl secured_cookie res.cook(JSESSIONID),lower -m sub secure
http-response replace-header Set-Cookie (.*) 1; Secure if https !secured_cookie
This assumes that the server sets up a single cookie.

Replace a particular value of a header field

Note: HTTP RFC specifies that the comma ‘,’ character is a Header field delimiter. It means that

X-Forwarded-For: 192.168.0.1, 10.0.0.1

is equivalent to:

X-Forwarded-For: 192.168.0.1
X-Forwarded-For: 10.0.0.1

HAProxy can update a Header field value while taking into account the comma ‘,’ character as a separator. This means that the update applies to each individual value.

http-response replace-value <name> <match-regex> <replace-fmt> [<condition>]

This directive expects the following parameters:

<name> the name of the header to update
<match-regex> a regex to match the content to update
<replace-fmt> a log format variable for the new value
<condition> (optional) a condition to apply this rule

Examples:

To insert a Secured flag on each cookie set up by the server:

http-response replace-value Set-Cookie (.*) 1; Secure

Rewrite Anywhere in the Response Using Regexes

The legacy statements rsprep and rspirep are still useful in cases not yet covered by http-request rules.

The statement rsprep applies a regex to each line of the request buffer in a case sensitive manner. As does rspirep, which is case insensitive:

rsprep  <search> <replace> [<cond>] rspirep <search> <replace> [<cond>]

This directive expects the following parameters:

<search> a regular expression to search the content to replace
<replace> a regular expression to apply the modification
[<condition>] (optional) a condition to apply this rule
HAProxy uses PCRE compatible regular expressions. For more information about PCRE syntax, see Regex Quick Start and Regex Cheat Sheet.

Examples:

To rewrite the Location from static.domain.com to www.domain.com when required:

acl h_static res.hdr(Location) -m sub static.domain.com
rspirep ^Location: http://static.domain.com(.*)      Location: http://www.domain.com1