Reverse Proxy with HAProxy

:warning: This guide has been migrated from our website and might be outdated. Feel free to edit this guide to update it, and to remove this message after that.

Using HAProxy to proxy for Home Assistant allows you to serve Home Assistant securely over standard ports with HTTP to HTTPS redirection.

Install HAProxy on your server

This will vary depending on your OS. Check out Google for this.

Obtain an SSL certificate

There are multiple ways of obtaining an SSL certificate. Let’s Encrypt is one method.
Use Google for this, but a good example of using Certbot can be found here.

HAPRoxy Configuration

The following configuration updates HAProxy defaults for more secure ciphers for SSL and logging and connection
timeouts.

Items to update for your deployment:

  • bind: Update the ports HAProxy listens on for forwarding.
  • subdomain.domain.com: Your domain to use
  • ssl crt: The path to your SSL certificate.
  • server hass 127.0.0.1:8123: The IP and port location of your Home Assistant instance.
global
	log /dev/log	local0
	log /dev/log	local1 notice
	chroot /var/lib/haproxy
	stats socket /run/haproxy/admin.sock mode 660 level admin
	stats timeout 30s
	user haproxy
	group haproxy
	daemon

	# Default SSL material locations
	ca-base /etc/ssl/certs
	crt-base /etc/ssl/private

	# Default ciphers to use on SSL-enabled listening sockets.
	# For more information, see ciphers(1SSL). This list is from:
	#  https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
	ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
	ssl-default-bind-options no-sslv3
	maxconn 2048
	tune.ssl.default-dh-param 2048

defaults
	log	global
	mode	http
	option	httplog
	option	dontlognull
	timeout connect 5000
	timeout client  50000
	timeout server  50000
	timeout tunnel  60000    # long enough for websocket pings every 55 seconds
	timeout http-request 5s  # protection from Slowloris attacks

frontend www-http
	bind *:80
	redirect scheme https

frontend www-https
	log /dev/log	local0 debug
	bind *:443 ssl crt /etc/haproxy/certs/MYCERT.pem
	acl hass-acl hdr(host) -i SUBDOMAIN.DOMAIN.COM
	use_backend hass-backend if hass-acl

backend hass-backend
	server hass <Home Assistant Server IP>:8123

	mode http
	option forwardfor
	http-request add-header X-Forwarded-Proto https
	http-request add-header X-Forwarded-Port 443

Forward Ports

Forward ports 443 and (optionally) 80 to your server on your router.

Do not forward port 8123, HAProxy takes care of securing the connection with HTTPS on 443.
If 8123 is forwarded then it will not be secured.

Replace 443 with whatever port you chose to bind to in the configuration if different.

Configure Home Assistant HTTP Component

In your configuration.yaml file, edit the HTTP component.

http:
  # For extra security set this to only accept a connection on localhost if HAProxy is on the same machine
  # server_host: 127.0.0.1
  # Update this line to be your domain
  use_x_forwarded_for: true
  # You must set the trusted proxy IP address so that Home Assistant will properly accept connections
  # Set this to your HAProxy machine IP, or localhost if hosted on the same machine.
  trusted_proxies: <HAProxy IP address here, 127.0.0.1 if same machine>

Restart or Reload HAProxy

Use your OS method of restarting or reloading HAProxy. Use Google for this.

Note that adding new X-Forwarded-* headers may cause issues with the latest home assistant:

In my case I was adding multiple X-Forwarded-Proto headers, so the solution was to remove the one in the HAProxy config above.

Because this seems the first hit on Google for HA and HAProxy I want to share something I recently had problems with. My connection to HA worked but the WebSocket connection was not possible.
It seems the WebSocket tries to connect to the same host but always with the port added to the request.
This results in HAProxy not able to direct the request to the designated backend if you just have an acl like this:

acl hass-acl hdr(host) -i SUBDOMAIN.DOMAIN.COM

I added a second acl:

acl hass-acl443 hdr(host) -i SUBDOMAIN.DOMAIN.COM:443

and then

use_backend hass-backend if hass-acl || hass-acl443

and WebSocket connections started to work…

Alex, how where do you do this setting, I’m using haproxy on pfSense.
I’m able to browser connect to my HA environment, but not from mobile device, it comes up with invalid cert.

using Cloudflare → edge modem->pfSense (haProxy/ACME cert)

Disabled reverse proxy on my url https://ha.“my-domain”.com

G

Sorry no idea, my solution is not for basic access but if you already have a working connection but no WebSocket.

thanks, no problem…

got mine working without all of this.

G

1 Like

Hi George,

I have same setup as you. Can you please share how you fix it. Thanks

id also be interested in setting this up, so appreciate a reply

I have the same problem. On my scenario I have Synology Surveillance Station configured and it is on top of HomeAssistant. Surveillance Station also uses websocket. I have to interchange the 2, making HomeAssistant on top of the Surveillance Station and it worked for me. So if you have any site that uses websocket place HomeAssistant on top of that site in the HAProxy ACL.