Haproxy redirect based on path
Similar to HAProxy reqrep remove URI on backend request.
The following concerns apply for us.
We have applications which were running with different context roots off one domain. However not all clients of the urls were changed.
I would like to redirect with a 301 redirect in haproxy if a request matches a legacy path.
Take for example http://example.com/abc
and http://example.com/def
...
frontend https
bind *:{{ proxy_port }} ssl crt /etc/haproxy/bundle_dh.pem ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4 no-sslv3
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-Port %[dst_port]
acl has_legacy_abc path_beg /abc
acl has_legacy_def path_beg /def
redirect location 301 https://abcdomain.com/{PATH_WITHOUT_ABC} if { has_legacy_abc }
redirect location 301 https://defdomain.com/{PATH_WITHOUT_ABC} if { has_legacy_def }
use backend abc_backend if { hdr(Host) -i abcdomain.com }
use backend def_backend if { hdr(Host) -i defdomain.com }
...
The problem is how to retain the path in the redirect. I can do an absolute redirect.
Looked and reqrep
but that seems to be for changing a request before passing to a backend. I want to tell all visitors they should go to the new domain.
Solution 1:
This can be done using some temporary headers like this, inserted between your acl
definitions, and use_backend
keywords:
http-request set-header X-Location-Path %[capture.req.uri] if has_legacy_abc OR has_legacy_def
http-request replace-header X-Location-Path [^/]+/(.*) \1 if has_legacy_abc OR has_legacy_def
http-request redirect location https://abcdomain.com/%[hdr(X-Location-Path)] if has_legacy_abc
http-request redirect location https://defdomain.com/%[hdr(X-Location-Path)] if has_legacy_def
Depending on the actual configuration of your domains/paths and required redirections, it might even be possible to collapse it all down into a single set of directives, like this:
...
frontend https
bind *:{{ proxy_port }} ssl crt /etc/haproxy/bundle_dh.pem ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4 no-sslv3
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-Port %[dst_port]
acl has_legacy path_beg /abc /def
http-request set-header X-Location-Path %[capture.req.uri] if has_legacy
http-request set-header X-Location-Host %[capture.req.uri] if has_legacy
http-request replace-header X-Location-Host /([^/]*)/ \1 if has_legacy
http-request replace-header X-Location-Path [^/]+/(.*) \1 if has_legacy
http-request redirect location https://%[hdr(X-Location-Host)]domain.com/%[hdr(X-Location-Path)] if has_legacy_abc
use backend abc_backend if { hdr(Host) -i abcdomain.com }
use backend def_backend if { hdr(Host) -i defdomain.com }
...
Should the number of paths and domains be huge, you can use maps (where the key is the path, and the value is the destination host) to simplify things even more.
Solution 2:
I've got the same issue and found another solution that I think is better. This other solution works well with version 1.6+ of haproxy. It uses http-request and regsub.
If I rewrite your example:
frontend https
bind *:{{ proxy_port }} ssl crt /etc/haproxy/bundle_dh.pem ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4 no-sslv3
acl has_legacy_abc path_beg /abc
acl has_legacy_def path_beg /def
http-request redirect code 301 location https://%[hdr(host)]%[url,regsub(^/abc,/newabc,)] if has_legacy_abc