Simple redirect to HAproxy backend servers
I want to configure simple redirect with HAproxy.
Desired behaviour: HAProxy receives request to example.com or https://example.com and returns https webserver start page from servers list excluding itself from a chain.
I tried with redirect location https://web1.com code 301 if example_acl
and it works for single server but I need 5 servers behind redirect, with web1.com, web2.com ... and round-robin. Maybe there is some specific config for backend section? please help
My current config is
-#------------------- GLOBAL SETTINGS ---------------------------
...
-#------------------- FRONTEND HTTP ---------------------------
frontend http_in
mode http
option httplog
bind *:80
stats enable
redirect scheme https if { hdr(Host) -i example.com }
-#------------------- FRONTEND TCP ---------------------------
frontend tcp_in
mode tcp
option tcplog
bind *:443
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
acl example_acl req.ssl_sni -i example.com
use_backend special_example if example_acl
-#------------------- Server Farms ---------------------------
backend special_example
mode tcp
balance roundrobin
?
Solution 1:
I agree with the commenter, this seems like an unusual application, but nevertheless, HAProxy comes through.
I've created a hosts file entry on my test proxy so that example.com actually points to 127.0.0.1 so the tests, below, are actual responses from the proxy.
Let's say I want example.com
to redirect to one of four servers, external-1.example.com
through external-4.example.com
.
This is a one-liner -- everything on one line, shown here as multiple lines for clarity.
This goes in the frontend
section.
http-request redirect code 301
location http://external-%[rand(4),add(1)].%[req.hdr(host)]%[url]
if { req.hdr(host) -m str example.com }
{ req.hdr(host) -m str example.com }
is an anonmyous ACL that enables the redirect if
it matches the current request. The location
is using the rand(n)
sample fetch, which generates a random number between 0 and n-1; it's followed by (passes its value to) the add(x)
converter, which adds x to the value -- in this case, we take possible values from 0 through (4 - 1) inclusive and add 1, so that the %[rand(4),add(1)]
expression evaluates to a number between 1 and 4 inclusive, which is implicitly cast to a string. We concatentate http://external-
with this integer, a dot, the incoming host header %[req.hdr(host)]
and the url %[url]
, which includes the path and query string.
Test 1:
$ curl -v http://example.com
* Rebuilt URL to: http://example.com/
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to example.com (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: example.com
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Content-length: 0
< Location: http://external-4.example.com/
< Connection: close
Test 2:
< HTTP/1.1 301 Moved Permanently
< Content-length: 0
< Location: http://external-2.example.com/
< Connection: close
Yep, that's working.
If you needed to redirect to a set of hostnames that weren't sequentially numbered, the solution would be similar, but you'd use http-request set-var
to store the random number in a transaction variable, then a series of redirect statements, one for each host, each of which with its own anonymous ACL that would evaluate the random value for equality to the integer matching that host, and redirect if the variable contained that value.
The rand()
fetch is not a random number generator of cryptographic quality, but certainly seems sufficient for this use case.