One port to catch HTTP and HTTPS requests and redirect to the HTTPS version

I have configuration that works well when HTTPS is in the URL but of course, when it is HTTP, it fails. The problem is, I must specify the port number in the URL. I am using this as a way to test individual servers. So the website name must remain unchanged to work with the SSL cert but I can assign one port (and an associated frontend and backend) in the haproxy.cfg file to route to the correct server.

The problem happens when for what ever reason, HTTPS is not specified but the magic port number is. Some apps do a redirect (code beyond my control) and because the server is only running on port 80 without SSL and haproxy is doing all the SSL work, the app doesn’t realize it needs to redirect to https://… so it just redirects to the http:// version and that is where things break.

My config entry…

frontend port_801
bind *:801 ssl crt /etc/ssl/private/unified-cert-file.pem
capture request header Host len 32
capture request header User-Agent len 90
default_backend server_1_through_801

backend server_1_through_801
option forwardfor
http-request add-header X-CLIENT-IP %[src]
server server1 192.168.10.101:80 check

Now, the question is, how do I make this work so if I specify http://mysite.com:801 that haproxy will still respond but auto redirect to https://mysite.com:801 without throwing a fit?

Any help would be greatly appreciated.

1 Like

Use a TCP frontend to differentiate between HTTP and SSL traffic, than recirculate the traffic to proper HTTP or HTTPS frontends.

Something like:

frontend port801_combined
    mode tcp
    bind :801
    tcp-request inspect-delay 2s
    tcp-request content accept if HTTP
    tcp-request content accept if { req.ssl_hello_type 1 }
    use_backend recir_http if HTTP
    default_backend recir_https

backend recir_http
    mode tcp
    server loopback-for-http abns@haproxy-http send-proxy-v2
backend recir_https
    mode tcp
    server loopback-for-https abns@haproxy-https send-proxy-v2

frontend fe-https
    mode http
    bind abns@haproxy-https accept-proxy ssl crt /etc/ssl/private/unified-cert-file.pem
    # whatever you need todo for HTTPS traffic
frontend fe-http
    mode http
    bind abns@haproxy-http accept-proxy
    # whatever you need todo for HTTP traffic
4 Likes

I will try this but I really don’t understand what it is trying to do because I can’t find any details on some of these settings.

Everything in the “frontend port801_combined” makes sense.

I don’t understand what the two backend blocks are doing

I don’t see where the other two frontend blocks even get used

Can you explain this a bit before I go sticking it into my config file?

The frontend port801_combined detects whether the incoming request is SSL/HTTPS or plaintext HTTP.
It forwards the traffic to backend recir_http if the it is plaintext HTTP.
Otherwise it forwards the traffic to backend recir_https.

The backend recir_http sends all the traffic to the frontend fe-http via the socket abns@haproxy-http.

The backend recir_https sends all the traffic to the frontend fe-https via the socket abns@haproxy-https.

Frontend fe-http receives plaintext HTTP traffic (therefor no “ssl” configuration there).

Frontend fe-https receives encrypted HTTPS traffic (therefor your ssl and certificate configuration belongs there).

You can work with the latter 2 frontends to do whatever you want.

1 Like

I see now. The bindings in the last two frontend blocks do the connection back to the backend blocks. Thanks for explaining that.

I will give it a try shortly and let you know how it goes.

Ultimately, I did some tweaking to include the port number into all of the reference points and because I have several servers that I want to be able to address individually, I needed to replicate this process once for each port 801, 802, 803, etc.

Feels clunky, but since these only exist for testing purposes and cannot be accessed from the world because of our firewall, this works pretty well. If there is a simpler way, would love to know it but this will do the trick for me.

frontend port801_combined
    mode tcp
    bind :801
    tcp-request inspect-delay 2s
    tcp-request content accept if HTTP
    tcp-request content accept if { req.ssl_hello_type 1 }
    use_backend recir_801_http if HTTP
    default_backend recir_801_https
backend recir_801_http
    mode tcp
    server loopback-for-http abns@801-haproxy-http send-proxy-v2
backend recir_801_https
    mode tcp
    server loopback-for-https abns@801-haproxy-https send-proxy-v2
frontend fe_801_https
    mode http
    bind abns@801-haproxy-https accept-proxy ssl crt /etc/ssl/private/unified-cert-file.pem
    capture request header Host len 32
    capture request header User-Agent len 90
    default_backend port_801
frontend fe_801_http
    mode http
    bind abns@801-haproxy-http accept-proxy
    capture request header Host len 32
    capture request header User-Agent len 90
    default_backend port_801
backend port_801
    option  forwardfor
    http-request add-header X-CLIENT-IP %[src]
    redirect scheme https code 301 if !{ ssl_fc }
    server server1 192.168.10.107:80 check

AAAAAHHHHH!!!

Ok, well I thought I was being smart. I copied the code which is working. Replaced every reference to 801 with 802 and HAPROXY won’t start.

I remove the new lines and it works again.

WHY???

Share the error message please?

I was tired and stupid…

I missed one label when switching 801 to 802 and caused a duplicate backend entry.

This morning I was looking it over and spotted it. So putting the port number into the labels and socket name works, baring human error when retyping the port numbers.

Thanks for all the help!

1 Like

Hi,

Based on the solution provided, is it fair to say that haproxy can handle situations where I want to achieve the following:
I have one server hosting an https site and as ssh site, and also using certbot to get/refresh ssl certs from letsencrypt (why I need to do this is a whole different story, will not go into that here).
The catch is that only ports 80 and 443 are accessible.
Would haproxy be able to handle it in a way that any http/s traffic to any of those two ports is routed to the correct backend in http/s mode, and all other traffic is forwarded to the ssh port at a tcp level?

Yes, it is.

Hi, I’m trying to do something similar using the HAProxy plugin on pfSense. I would like to listen to for example port 8000 (sub1.domain.tld:8000) and if the traffic is plaintext http, redirect it to https. The problem is that for the GUI the steps aren’t really the same.

The following has already been done:

We are listening on different ports and proxying the traffic internally. For example when we type https://sub1.domain.tld:8000 it will redirect to the application and do an ssl offloading. The issue is that when I type http://sub1.domain.tld:8000 it doesn’t redirect to https://sub1.domain.tld:8000. The redirection only works when port 80 is being redirected to 443.

Grateful if you could please assist on this issue.

We can help you with haproxy. Cannot help you with your pfsense GUI though.

Share the current haproxy configuration as well as the output of haproxy -vv and we may be able to suggest what should be done on a configuration level.

Could you please provide the final code that worked for you?

Sorry, my config has changed quite a bit since this issue so i am not able to give a cide sample any more.

reposting here expecting some expert help as nobody even viewed my query.

I have two incoming requests, one of it needs to go to a “http:// server” and other to “https:// server”.
Below is my acl and backend definitions

------in haproxy_peers.cfg---------
acl downstream_service1 path_beg /downstream_serv1/internal/api
acl downstream_service2 path_beg /downstream_serv2/internal/api

use_backend downstream_serv1_http if downstream_service1
use_backend downstream_serv2_https if downstream_service2
------------------------------------------

backend configs:

--------in backend config file-----------
backend downstream_serv1_http
    http-request set-path "%[path,regsub(^/downstream_serv1/,/)]"
        server  downstream_server1   host-name.domain:port_number check         //*note there is no http/https protocol mentioned.*

backend downstream_serv2_https
    http-request set-path "%[path,regsub(^/downstream_serv2/,/)]"
        server  downstream_server2   host-name2.domain:port_number check      //*note there is no http/https protocol mentioned.*
---------------------------------------------

Expectation is
http://haproxyHost/downstream_serv1/internal/api should be routed to http://host-name.domain:port_number/internal/api

http://haproxyHost/downstream_serv2/internal/api should be routed to https://host-name2.domain:port_number/internal/api

With the above config im getting a 503/502 for the https route. HTTP route is working as expected…Kindly help.