HAProxy reqrep not replacing string in url - haproxy

We have deprecated some software which was available under http://localhost/test and is now available under http://localhost/app/testing. We are using HAProxy 1.4
Thus, I want via haproxy, to replace all urls containing /test with /app/testing/. I tried first with a redirect prefix in order to keep the query string, but /test wasn't removed from the url and had something like /app/testing/test/?id=x.
frontend all
bind 0.0.0.0:80
timeout client 86400000
acl is_test path_beg /test
redirect prefix /app/testing code 301 if is_test
Then used a reqrep, which seems to redirect to the new software, but the /test string in the url is never replaced.
frontend all
bind 0.0.0.0:80
timeout client 86400000
reqrep ^([^\ :]*)\ /test[/]?(.*) \1\ /app/testing/\2

Since url rewriting isn't possible with version 1.4 and we didn't want to update HAProxy, we went on using reqrep and keeping the old link as is with
reqrep ^([^\ :]*)\ /test[/]?(.*) \1\ /app/testing/\2

Try this
This works for me with your scenario on HAProxy 1.6
acl test path_beg -i /test
http-request set-header X-Location-Path %[capture.req.uri] if test
http-request replace-header X-Location-Path /test /app/testing if test
http-request redirect location %[hdr(X-Location-Path)] if test
use_backend WHEREVER if test

Using redirect prefix is meant, going by the HAProxy examples, more for changing the hostname, but keeping the path. For example, http://apple.com/my/path could be redirected to http://orange.com/my/path with:
redirect prefix http://orange.com if apple
The HAProxy 1.4 docs say:
With "redirect prefix", the "Location" header is built from the
concatenation of < pfx > and the complete URI path
To me, that suggests that you would expect whatever you put for the "prefix" will be prefixed to what was already in the path. That explains the behavior you were seeing. This is useful for changing to a new domain (e.g. from apple.com to orange.com), but keeping the original path (e.g. /my/path).
You can switch to using redirect location to replace the entire URL. The following would redirect http://apple.com/my/path to http://orange.com/path:
redirect location http://orange.com/path if apple
UPDATE:
In the case where you want to change the URL, but keep the query string, use reqirep or reqrep to rewrite the URL, as you are doing, but also put a redirect prefix into the frontend. The URL will be rewritten and then the user will be redirected to it so they see it.
You might be able to set the "prefix" to "/". The HAProxy docs say:
As a special case, if < pfx > equals exactly "/", then nothing is
inserted before the original URI. It allows one to redirect to the
same URL (for instance, to insert a cookie).
Using your code example, something like this:
reqrep ^([^\ :]*)\ /test[/]?(.*) \1\ /app/testing/\2
redirect prefix / code 301 if is_test

Related

Haproxy - 301/302 redirect URL1 to URL2 with all pathes

I'm a little bit lost atm. I try to implement a redirect within a complex HAproxy configuration. The goal is simple:
user uses a subdomain -> 123.domain.com
user will be pointed to api.domain.com but thinks it's still on 123.domain.com
user should be able to use pathes like 123.domain.com/123?123 but still get results from api.domain.com/123?123 but thinks it's gets result from 123.domain.com/123?123.
I'm totally unsure how to implement that without taking the rist of taking down production traffic.
What i would do:
creating ACL rule in SSL frontend to point to the api backend when 123.domain.de is used.
redirect prefix http://123.domain.com code 301 if { hdr(host) -i api.domain.com }
Not usre if that would work.
creating redirection rule pointing simply to another api url:
redirect location https://www.mysites/v2/pages 302 if { hdr(host) -i api.domain.com }
It's hard to implement this wihtout taking a risk of an outage. Is there something who could know the answer?
First I would recommend that you have a development and/or staging environment configured to test any changes before you make them.
To do a 301 redirect of all traffic coming from 123.domain.com to api.domain.com you can use the following
http-request redirect prefix http://api.domain.com code 301 if { hdr(host) -i 123.domain.com }
If you wanted HAProxy to connect to the api.domain.com backend on the users behalf and mask the hostname itself you would add a backend for api.domain.com and then create a use_backend rule. Keep in mind that with the below there are no 301/302 redirects performed, instead HAProxy makes the connection on behalf of the client.
Something like this would work:
use_backend api.domain.com if { hdr(host) -i 123.domain.com }
Then within api.domain.com backend you can update the Host header.
backend api.domain.com
http-request set-header Host api.domain.com
server api1 api.domain.com:80 check

HAProxy - Add Trailing Slash

I am working on a project where I need requests destined to a particular page to be routed to a separate backend.
For example, all requests for https://mycooldomain.com will go to backend "A". But, if navigating to https://mycooldomain.com/secretpage I want it to go to backend "B".
Now, I have this working but running into an issue where I need the trailing slash for this to work correctly.
So, I need a way to say if request is https://[whateverhostnameisused]/secretpage redirect to https://[whateverhostnameisused]/secretpage/.
Here is a sample of my config so far:
frontend f_https
bind *:443 ssl crt cert.pem
reqadd X-Forwarded-Proto:\ https
#define hosts
acl host_a hdr(host) -i a.mycooldomain.com
acl host_b hdr(host) -i b.mycooldomain.com
acl host_c hdr(host) -i c.mycooldomain.com
#custom acls
acl secret path_beg -i /secretpage
#Custom redirects
##define backend
use_backend b_secret if secret
use_backend b_a if host_a
use_backend b_b if host_b
use_backend b_c if host_c
default_backend b_https
backend b_secret
server secret 192.168.15.15:5575 check
It seems that you are looking for something like this:
http-request redirect scheme https drop-query append-slash if { path -m str /secretpage }
This should work if applied to either the frontend or the backend.
http://cbonte.github.io/haproxy-dconv/1.6/configuration.html#4.2-redirect%20scheme
Specifying the scheme is only necessary because the syntax requires one of location | prefix | scheme, and with the other two options, you have to reassemble the URL yourself in the config.
Note also that reqadd is not officially deprecated, but the preferred way to add that request header is like this:
http-request set-header X-Forwarded-Proto https
Note that no : is specified and the space after the header name must not be escaped with \. This accomplishes the same result, but it uses a different code path inside HAProxy, and should be a more efficient operation. You will want to use the the http-request and http-response directives instead of reqxxx and rspxxx where possible, as they are also better suited to more complex manipulations.

How can I redirect specific HTTPS request to a backend server using HAPROXY

I have a HTTPS server and want to redirect the specific request based on the URL to my Backend server.
Something like this should do it (for HTTP):
frontend http
bind *:80
acl mpd path_end -i .mpd
acl test hdr(host) test.com www.test.com
use_backend internal if test mpd
backend internal
http-request set-path /path/folder%[path]
# for older versions
# reqirep ^([^\ :]*)\ \/(.+\/)*(.*)\ \1\ /path/to/folder/\3
server internal-1 internal:80 check
Two ACLs in the frontend select the right backend and the http-request (reqirep for older versions) command overwrites the original request path.
You might need to tweak it.

redirect all root domain request to www. subdomain, keeping the URL intact

I want to redirect all traffic to root domain to the www. version while still keeping path/query string intact, how can I do that? I have setup a HAproxy config like follows:
frontend http
bind *:80
bind *:443
acl has_www hdr_beg(host) -i www
http-request redirect code 301 location http://www.%[hdr(host)]%[req.uri] unless has_www
However this one does the following: example.com/abc?page=1 => www.example.com, where I actually wan't it to do: example.com/abc?page=1 => www.example.com/abc?page=1 - what am I missing?
if you are using HAProxy 1.5 and up this should work for you
Single domain :
acl has_www hdr_beg(host) -i www
redirect prefix http://www.example.com code 301 unless has_www
Multiple domains: (dirty but works)
# match non www requests:
acl has_www hdr_beg(host) -i www.
# add a header that says we need to redirect these type of requests (will explain later)
http-request add-header X-Host-Redirect yes unless has_www
# rule to identify newly added headers
acl www_redirect hdr_cnt(X-Host-Redirect) eq 1
# where the magic happens (insert www. in front of all marked requests)
reqirep ^Host:\ (.*)$ Host:\ www.\1 if www_redirect
# now hostname contains 'www.' so we can redirect to the same url
redirect scheme http if www_redirect
The reason why we add a new header is because it seems that in HAProxy 1.5 and up, acls are being evalutated on each reference. so when you try to do this without the new header it does this :
#catch all domains that begin with 'www.'
acl has_www hdr_beg(host) -i www.
#insert www. in front of all non www requests
reqirep ^Host:\ (.*)$ Host:\ www.\1 unless has_www
#now hostname contains 'www.' so when the next rule gets interpreted it does not match then fails
#redirect to new url
redirect code 301 prefix / if has_www
hope this helps. I've tested this with 5 different domains on HAProxy 1.5 and it worked fine and kept the query strings intact on redirect
A multi-domain solution for HAProxy 1.6 which redirects preserving the path and query parameters:
frontend 443
http-request redirect prefix https://www.%[hdr(host)] code 301 unless { hdr_beg(host) -i www. }

Haproxy redirect www to non-www

I'm currently using Haproxy to balance several express.js nodes. I know that it's possible to redirect using express.js, but I was hoping to do so with Haproxy.
I was wondering how I can do a permanent redirect from www.mysite.com to mysite.com?
redirect prefix http://example.com code 301 if { hdr(host) -i www.example.com }
Please see the documentation of the redirect prefix rule for more information.
If you are using a newer version of HAProxy, i.e. at least 1.6, you can use a more generic syntax which allows to redirect any host, not just explicitly named
http-request redirect prefix http://%[hdr(host),regsub(^www\.,,i)] code 301 if { hdr_beg(host) -i www. }
Here, we are using the regsub filter to dynamically generate the correct hostname without the www. prefix.
In case you want to perform a redirect the other way around, i.e. to add a www if there is none already, the rule becomes simpler:
http-request redirect prefix http://www.%[hdr(host)] code 301 unless { hdr_beg(host) -i www. }