HAProxy 1.6 redirect using http-response, map and regsub

haproxyredirect

I want to setup a redirect map similar to this https://stackoverflow.com/questions/23001799/how-do-i-used-the-map-feature-in-haproxy-to-build-massive-redirect-tables-1-5

The difference is I want to use http-response instead of http-request. The reason being is because I want to redirect only when backend server returns 404.

This is my config

http-response redirect location %[capture.req.uri,regsub(\?(.*),),map(/etc/haproxy/redirects.map)] code 301 if { status 404 } { capture.req.uri,regsub(\?(.*),),map(/etc/haproxy/redirects.map) -m found }

I tried to use regsub to remove query params from capture.req.uri. But, I get this error when restarting HAProxy.

[ALERT] 280/171612 (6176) : parsing [/etc/haproxy/haproxy.cfg:87] : error detected in proxy 'http' while parsing 'http-response redirect' rule : error in condition: invalid arg 2 in conv method 'regsub' : missing arguments (got 1/2), type 'string' expected in ACL expression 'capture.req.uri,regsub(\?(.*),),map(/etc/haproxy/redirects.map)'.
[ALERT] 280/171612 (6176) : Error(s) found in configuration file : /etc/haproxy/haproxy.cfg
Errors found in configuration file, check it with 'haproxy check'.

Is there a way to get the URL without query params ? I tried to use path instead of capture.req.uri but HAProxy won't start.

This is my config using path

http-response redirect location %[path,map(/etc/haproxy/redirects.map)] code 303 if { status 404 } { path,map(/etc/haproxy/redirects.map) -m found }

And this is the warning

[WARNING] 283/090721 (2875) : parsing [/etc/haproxy/haproxy.cfg:88] : 'redirect' : sample fetch <path,map(/etc/haproxy/redirects.map)> may not be reliably used here because it needs 'HTTP request headers' which is not available here.
[WARNING] 283/090721 (2875) : parsing [/etc/haproxy/haproxy.cfg:88] : anonymous acl will never match because it uses keyword 'path' which is incompatible with 'backend http-response header rule'

Best Answer

The original issue was a problem with regsub(\?(.*),), which caused a problem because the the regsub converter is limited to expressions the configuration parser can handle -- and parentheses aren't usable because the parser sees the ) as closing regsub() with too few arguments. (For literals, you can use \\xnn hex-escapes to work around parser limitations, but that wouldn't work here.)

regsub was being used because this redirect is being triggered during response processing if { status 404 }, and the path fetch is not available by that stage of processing -- HAProxy frees the buffers used to by the request once it is sent to a server.

However, HAProxy 1.6 also introduces user variables that can be used to carry data across from the request side if used in transaction (txn) scope.

During request processing, stash the contents of the path fetch in a transaction-scoped variable called (coincidentally) path.

http-request set-var(txn.path) path

Then, it can be accessed during response processing.

The following is shown on multiple lines for clarity but must be on a single line of configuration.

http-response redirect 
    location %[var(txn.path),map(/etc/haproxy/redirects.map)] 
    code 303 
    if { status 404 } { var(txn.path),map(/etc/haproxy/redirects.map) -m found }

This -- if the response status code is 404 -- fetches the value back out of the variable and checks to see if it has a value in the map file. If so, that value is used for the redirect.

Related Topic