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

| improve this question | | | | |
  • 1
    Normally you would put the 5 servers in a backend and round robin load balance them i.e. use_backend my5servers if has_special_url... Or do you literally mean you want to round robin re-direct to five DIFFERENT URLs? Which is just weird. – Malcolm turnbull Jun 10 '16 at 16:18

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 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
* Connected to example.com ( 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.

| improve this answer | | | | |
  • Very good idea. I configured acl example_acl hdr(Host) -i example.com !{ssl_fc} http-request set-var(req.my_var) rand(2),add(1) if example_acl acl var_1 var(req.my_var) eq 1 acl var_2 var(req.my_var) eq 2 http-request redirect code 301 location https://exampleA.com if var_1 http-request redirect code 301 location https://exampleB.com if var_2 But no health check like in backend section. – Oleg Doroshenko Jun 30 '16 at 14:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.