Nginx Reverse Proxy Set Up Guide – Docker

This is the best explanation I found so far, look like it required

There appears to be a new “block” on reverse proxies that will require an extra config entry for this Nginx Reverse Proxy to work properly. According to the latest release notes - 2021.6: A little bit of everything - Home Assistant

HTTP (using reverse proxies)

Home Assistant will now warn when a misconfigured reverse proxy, or misconfigured Home Assistant instance when using a reverse proxy, has been detected.

These warnings will become an error in Home Assistant 2021.7.

If you are using a reverse proxy, and see these warnings, please make sure you have configured use_x_forwarded_for and trusted_proxies in your HTTP integration configuration.

For more information, see the HTTP integration documentation.

(@frenck - #51332)

I had to add the following to my config using this NGINX/Swag container to clear the log error:

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.21.0.2

Not exactly sure why I had to use 172.21.0.2, when most posting on this thread Reverse proxy error needed 172.21.0.1 . The 172.21.0.2 is listed in Portainer under “networks” and then when I click the “swag container” it is towards the bottom. Maybe someone who knows a little more about Docker and networks can chime in on why.

Posting this here as according to the log error, its a warning for now, but coming in the July release, it will block reverse proxy requests if you do not set the http options in the config.yaml file like mentioned here - HTTP - Home Assistant . I get this is an important security update, but it definitely adds another layer of complication to setting up a reverse proxy when running Home Assistant in a container and I imagine a lot of people will be caught off guard by it when it just stops working next month without the extra http config settings being added.

Update - I also wanted to chime in on the fastcgi settings. I saw that someone posted about it recently above, and in the beginning I always saw fastcgi errors in my nginx error logs, and also noticed a lot of issues with my Ring and Blink integrations which I believe use fastcgi to send videos to home assistant. I previously had changed this to the IP address of the host machine which was causing the issues. Aparently NGINX can’t handle fastcgi on its own, so needs a “helper” which in this case is PHP. The Swag container has PHP and can actually handle fastcgi requests within its own port 9000. So, in the default config, the fast CGI is setup this way:

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        include /etc/nginx/fastcgi_params;
    }

You DO NOT want to change anything in this block. Do not change the IP address of 127.0.0.1 to your host machine. This IP address is the “loopback” or “home” address of the actual Swag container itself, and is necessary to reference PHP for fastcgi to process properly. If you change it to your hostip (192.168.0.whatever), fastcgi requests won’t work because you’re not maping port 9000 out of the Swag container (only port 443 which is Https and port 80 which is http are mapped out) and not getting it to the right place anymore. Since port 9000 is not mapped out of the Swag container it will only exist locally within that container and not outside it, and even if you run portainer mapped to port 9000 on the host machine, you still should not have a conflict with this setting.

The specifics under 172.16.0.0/12 network would be different from one setup to another - depends on your environment and how you do your dockers or install types.

So maybe consider this?

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 127.0.0.1       # in case the reverse proxy being on the same IPv4 as the HA
    - ::1             # in case the reverse proxy being on the same IPv6 as the HA
    - 172.16.0.0/12   # in case the reverse proxy being under the same docker environment as the HA, and in case the docker IP would change after reboot/restart
    - xxx.xxx.xxx.xxx # specifying the IP of the reverse proxy, if none of the above applies

… and maybe one would need multiple lines under trusted_proxies: to work, depends on the setup.

Thanks @k8gg . So I never really payed attention to docker network settings before, but with the new requirement to add the IP address of the trusted proxy to Home Assistant’s config now it matters. If that trusted IP is wrong the reverse proxy will fail on the next release.

So here are my Docker network settings for all my containers:

And specifics for the Swag container

The 172.21.0.2 is what I had to add to my home assistant config as a trusted proxy which was specified in the swag_default network. This cleared any log warnings about the proxy. Based on your post, I assume these IP’s are going to be different for everyone depending on the number of containers/installation order or otherwise somehow specifying network settings in docker. My questions would be:
1- Will these IP’s randomly change for the containers if not “statically” set somehow - ie upon docker restart, other containers restarting, or deleting/adding the containers like after an image is updated?
2- If they might change, how should I set them up so the IP addresses/network settings stay the same all the time? Anything that I can just configure right through portainer?

Gents, i dont believe the solution is to widen the trusted_proxies: list. Seems counter intuitieve.
What I did was give the proxy (here swag) a static address like so:

    networks:
      mynet:
        ipv4_address: 172.10.0.10    # set up static ip to prevent HA blocking

Add that snipet to corresponding docker-compose entry, associated to a docker network I created with the following:

networks:
  mynet:  # set up static ip to prevent HA blocking
    driver: bridge
    driver_opts:
      com.docker.network.enable_ipv6: "false"
    ipam:
      driver: default
      config:
      - subnet: 172.10.0.0/24 # 

then add only this line:

  trusted_proxies:
  - 172.10.0.10 #as set up in docker-compose for Nginx/Letsencrypt
  - ::1
1 Like

Thanks @juan11perez . I was having some issues with trying to change the IP address to something new, thinking it’s probably browser and app caching issues of the old settings, or it was conflicting with something else. I ended up just specifying the IP address of 172.21.0.2 in the compose for the default swag network (which is what it was randomly given already) so it remains static and won’t change on me upon restarts/re-installs based on your post. Hopefully I’m not missing something here, but my whole compose if it helps:

version: "2.1"
services:
  swag:
    image: linuxserver/swag
    container_name: swag
    restart: unless-stopped
    cap_add:
    - NET_ADMIN
    volumes:
    - /home/tim/docker/swag/config:/config
    - /etc/localtime:/etc/localtime:ro
    environment:
    - PGID=1000
    - PUID=1000
    - [email protected]
    - URL=yourdomain.duckdns.org
    - SUBDOMAINS=wildcard
    - VALIDATION=duckdns
    - TZ=yourtimezone
    - DUCKDNSTOKEN=yourtoken
    - MAXMINDDB_LICENSE_KEY=yourkey #this is optional for location based IP banning
    ports:
    - "80:80"
    - "443:443"
    networks:
      default:
        ipv4_address: 172.21.0.2

Then these lines in my home assistant config

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.21.0.2

I left out ::1 since I’m not using IPV6 at all for anything, but may need to add that later

1 Like

Hi all!
First of all thank @juan11perez for the great guide!
Unfortunately I’m facing some issues with setting this thing up.

I got the swag container running and I can acces the nginx main page from mydomain.duckdns.org.
But, when I try to access homeassistant by going to hass.mydomain.duckdns.org I’m getting a 504 Gateway Time-Out error…

This is my docker-compose setup -

version: '3'
services:
  homeassistant:
    container_name: hass
    image: homeassistant/home-assistant
    volumes:
      - ./hass-config:/config
      - /etc/localtime:/etc/localtime:ro
    restart: unless-stopped
    network_mode: host
    depends_on:
      - mariadb
      - mosquitto
  swag:
    image: linuxserver/swag
    container_name: swag
    restart: unless-stopped
    cap_add:
    - NET_ADMIN
    volumes:
    - /etc/localtime:/etc/localtime:ro
    - /home/pi/homeassistant/swag/config:/config
    environment:
    - PGID=${PGID}
    - PUID=${PUID}
    - [email protected]
    - URL=mydomain.duckdns.org
    - SUBDOMAINS=wildcard
    - VALIDATION=duckdns
    - TZ=Asia/XXXX
    - DUCKDNSTOKEN=XXXXX
    ports:
    - "80:80"
    - "443:443"
    networks:
      default:
        ipv4_address: 172.10.0.100

And this is my default nginx file -

## Version 2021/04/27 - Changelog: https://github.com/linuxserver/docker-swag/commits/master/root/defaults/default

error_page 502 /502.html;
server_names_hash_bucket_size  64;

# redirect all traffic to https
server {
    listen 80;
    server_name mydomain.duckdns.org;
    return 301 https://$host$request_uri;
}

# main server block
server {
    listen 443 ssl default_server;

    root /config/www;
    index index.html index.htm index.php;

    server_name mydomain.duckdns.org;

    # enable subfolder method reverse proxy confs
    include /config/nginx/proxy-confs/*.subfolder.conf;

    # all ssl related config moved to ssl.conf
    include /config/nginx/ssl.conf;

    client_max_body_size 0;

    location / {
        try_files $uri $uri/ /index.html /index.php?$args =404;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
	    fastcgi_pass hostip:9000;
        #fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        include /etc/nginx/fastcgi_params;
    }

}
server {
	listen 443 ssl;

	root /config/www;
	index index.html index.htm index.php;

	server_name hass.mydomain.duckdns.org;

	include /config/nginx/ssl.conf;

	client_max_body_size 0;

	location / {
#		auth_basic "Restricted";
#		auth_basic_user_file /config/nginx/.htpasswd;
		proxy_set_header Host $host;
		proxy_redirect http:// https://;
		proxy_http_version 1.1;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
		proxy_buffering               off;
		proxy_ssl_verify              off;
#		include /config/nginx/proxy.conf;
		proxy_pass http://hostip:8123;
	}
}



# enable subdomain method reverse proxy confs
include /config/nginx/proxy-confs/*.subdomain.conf;
# enable proxy cache for auth
proxy_cache_path cache/ keys_zone=auth_cache:10m;


I also added the following in my home assistant config -

http:
  ip_ban_enabled: true
  login_attempts_threshold: 3
  use_x_forwarded_for: true
  base_url: hass.mydomain.duckdns.org
  trusted_proxies:
    - 192.168.31.0/24  # Local Lan
    - 172.10.0.0/24  # Docker network

What am I doing wrong?

Thanks :slight_smile:

not sure this is your problem, but base_url does not go under http. You need to remove that.

you probably need to add the following:

  internal_url: http://192.168.1.xx:8123 # introduced with HA 110.0
  external_url: hass.mydomain.duckdns.org

it goes under homeassistant

unfortunately it’s still not working… I think it’s something to do with swag…
home assistant is using network_mode = host while swag sits on the docker network…
maybe I didn’t set this up correctly and swag just can’t reach the host network?

this is the error I get in swag’s logs -

2021/07/08 15:49:42 [error] 502#502: *16 upstream timed out (110: Operation timed out) while connecting to upstream, client: 192.168.31.1, server: hass.mydomain.duckdns.org, request: "GET / HTTP/1.1", upstream: "http://192.168.31.5:8123/", host: "hass.mydomain.duckdns.org"

On this line in your nginx config towards the end to reference home assistant:

proxy_pass http://hostip:8123

Are you actually putting in the ip address of the host machine or leaving it “hostip”? You cannot reference it by “hostip” since Home-assistant runs in host network mode and swag doesn’t. In host mode, home assistant is not running on the same docker network as swag/nginx. If you have a container in bridge network mode (like swag) you can’t reference another docker container running in host network mode (like home assistant) by 127.0.0.1, localhost, hostip, or container name. You have to type out the whole ip of the host machine home assistant is running on, like

proxy_pass http://192.168.0.146:8123

Replacing with your machine’s ip on your lan of course.

If you are spelling out the IP and it still doesn’t work, are there any log errors when you startup swag?

Also, in Home assistant config, just try to reference the container’s IP itself and not the whole docker network. Not sure if it matters, but that’s how mine is setup and working with the new proxy block implemented in the 7.2021 release.

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.10.0.100

thanks @mwav3 for the reply…

I am using the full ip of home assistant (192.168…)
As for the logs when swag startsup, there are no errors there… and as I mentioned, I can indeed access the swag main page by going to mydomain.duckdns.org without the subdomain at the start.

Tried setting a constant ip in the http trusted_proxies section of home-assistant… still doesn’t work :confused:

Its probably just the default NGINX default conf file. A lot in what you posted looks different then mine. You can try mine if it helps. Just replace your “mydomain.duckdns.org” with your actual duckdns domain and the IP address under Home Assistant with your actual machine’s IP address. Leave all the other IP references alone as those are loopbacks within the Swag container itself (for fast CGI and resolver)

## Version 2020/05/23 - Changelog: https://github.com/linuxserver/docker-swag/commits/master/root/defaults/default

# redirect all traffic to https
server {
	listen 80 default_server;
	listen [::]:80 default_server;
	server_name mydomain.duckdns.org;
	return 301 https://$host$request_uri;
}

# main server block
server {
	listen 443 ssl http2 default_server;
	listen [::]:443 ssl http2 default_server;

	root /config/www;
	index index.html index.htm index.php;

	server_name mydomain.duckdns.org;
	
	# enable subfolder method reverse proxy confs
	include /config/nginx/proxy-confs/*.subfolder.conf;

	# all ssl related config moved to ssl.conf
	include /config/nginx/ssl.conf;

	# enable for ldap auth
	#include /config/nginx/ldap.conf;

	# enable for Authelia
	#include /config/nginx/authelia-server.conf;

	# enable for geo blocking
	# See /config/nginx/geoip2.conf for more information.
	#if ($allowed_country = no) {
	#return 444;
	#}

	client_max_body_size 0;

	location / {
		try_files $uri $uri/ /index.html /index.php?$args =404;
	}

	location ~ \.php$ {
		fastcgi_split_path_info ^(.+\.php)(/.+)$;
		fastcgi_pass 127.0.0.1:9000;
		fastcgi_index index.php;
		include /etc/nginx/fastcgi_params;
	}


}


### HOMEASSISTANT ##############################################################
server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name hass.*;
    
    include /config/nginx/ssl.conf;

    client_max_body_size 0;

    # enable for ldap auth, fill in ldap details in ldap.conf
    #include /config/nginx/ldap.conf;

    location / {
        # enable the next two lines for http auth
        #auth_basic "Restricted";
        #auth_basic_user_file /config/nginx/.htpasswd;

        # enable the next two lines for ldap auth
        #auth_request /auth;
        #error_page 401 =200 /login;

        include /config/nginx/proxy.conf;
        resolver 127.0.0.11 valid=30s;
        set $upstream_app homeassistant;
        set $upstream_port 8123;
        set $upstream_proto http;
        proxy_pass http://192.168.0.184:8123;

    }

    location /api/websocket {
        resolver 127.0.0.11 valid=30s;
        set $upstream_app homeassistant;
        set $upstream_port 8123;
        set $upstream_proto http;
        proxy_pass http://192.168.0.184:8123;

        proxy_set_header Host $host;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
# enable subdomain method reverse proxy confs
include /config/nginx/proxy-confs/*.subdomain.conf;
# enable proxy cache for auth
proxy_cache_path cache/ keys_zone=auth_cache:10m;
1 Like

Does anyone have this working still in Home Assistant 2021.7? I’ve added the


http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.18.0.18:

To my config and it works in 2021.6.6 but not with 2021.7 I keep getting a 400 Bad Request. I’m guessing I need to tweak something in the NGINX now but after spending a few hours on it I’ve given up and just went back to 2021.6.6. Curious if any of you guys had to make any changes to keep it working going to 2021.7?

Yes it is working fine for me still with 2021.7. Take a look at my posts above with my docker compose, NGINX config, and home assistant config to see if you have any differences you need to change. The only thing that I had to add were the same lines you just posted to the home assistant config for http: settings to keep this working. It’s possible there was something else missing in your docker compose or NGINX config though that didn’t matter before but is creating a problem for you now.

1 Like

… actually I did get it to work with your config in NGINX Proxy Manager, I just put the entire locations in the Advanced section of the Proxy Host in Nginx Proxy Manager and then it came back online and was able to pass the traffic correctly. I was trying to just put the individual nginx variables at first. This is what I put in my Advanced section and it starting working again. Thanks for the quick confirmation that it works and for providing your example!

location / {
        set $upstream_app homeassistant;
        set $upstream_port 8123;
        set $upstream_proto http;
        proxy_pass http://192.168.1.104:8123;

    }

    location /api/websocket {
        set $upstream_app homeassistant;
        set $upstream_port 8123;
        set $upstream_proto http;
        proxy_pass http://192.168.1.104:8123;

        proxy_set_header Host $host;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
4 Likes

@mwav3 Still nothing…
I set up a new home assistant instance from scratch without all the other services I use to see if that’s the issue but I get the same results…

I used your docker-compose example and your swag config -

docker-compose -

version: '3'
services:
  homeassistant:
    container_name: hass
    image: homeassistant/home-assistant
    volumes:
      - ./hass-config:/config
      - /etc/localtime:/etc/localtime:ro
    restart: unless-stopped
    network_mode: host
  swag:
    image: linuxserver/swag
    container_name: swag
    restart: unless-stopped
    cap_add:
    - NET_ADMIN
    volumes:
    - ./swag/config:/config
    - /etc/localtime:/etc/localtime:ro
    environment:
    - PGID=1000
    - PUID=1000
    - [email protected]
    - URL=MYDOMAIN.duckdns.org
    - SUBDOMAINS=wildcard
    - VALIDATION=duckdns
    - TZ=yourtimezone
    - DUCKDNSTOKEN=MYTOKEN
    ports:
    - "80:80"
    - "443:443"
    networks:
      default:
        ipv4_address: 172.11.0.2

networks:
  default:  # set up static ip to prevent HA blocking
    driver: bridge
    driver_opts:
      com.docker.network.enable_ipv6: "false"
    ipam:
      driver: default
      config:
      - subnet: 172.11.0.0/24 # 

swag default -

## Version 2020/05/23 - Changelog: https://github.com/linuxserver/docker-swag/commits/master/root/defaults/default

# redirect all traffic to https
server {
	listen 80 default_server;
	listen [::]:80 default_server;
	server_name MYDOMAIN.duckdns.org;
	return 301 https://$host$request_uri;
}

# main server block
server {
	listen 443 ssl http2 default_server;
	listen [::]:443 ssl http2 default_server;

	root /config/www;
	index index.html index.htm index.php;

	server_name MYDOMAIN.duckdns.org;
	
	# enable subfolder method reverse proxy confs
	include /config/nginx/proxy-confs/*.subfolder.conf;

	# all ssl related config moved to ssl.conf
	include /config/nginx/ssl.conf;

	# enable for ldap auth
	#include /config/nginx/ldap.conf;

	# enable for Authelia
	#include /config/nginx/authelia-server.conf;

	# enable for geo blocking
	# See /config/nginx/geoip2.conf for more information.
	#if ($allowed_country = no) {
	#return 444;
	#}

	client_max_body_size 0;

	location / {
		try_files $uri $uri/ /index.html /index.php?$args =404;
	}

	location ~ \.php$ {
		fastcgi_split_path_info ^(.+\.php)(/.+)$;
		fastcgi_pass 127.0.0.1:9000;
		fastcgi_index index.php;
		include /etc/nginx/fastcgi_params;
	}


}


### HOMEASSISTANT ##############################################################
server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name hass.*;
    
    include /config/nginx/ssl.conf;

    client_max_body_size 0;

    # enable for ldap auth, fill in ldap details in ldap.conf
    #include /config/nginx/ldap.conf;

    location / {
        # enable the next two lines for http auth
        #auth_basic "Restricted";
        #auth_basic_user_file /config/nginx/.htpasswd;

        # enable the next two lines for ldap auth
        #auth_request /auth;
        #error_page 401 =200 /login;

        include /config/nginx/proxy.conf;
        resolver 127.0.0.11 valid=30s;
        set $upstream_app homeassistant;
        set $upstream_port 8123;
        set $upstream_proto http;
        proxy_pass http://192.168.31.5:8123;

    }

    location /api/websocket {
        resolver 127.0.0.11 valid=30s;
        set $upstream_app homeassistant;
        set $upstream_port 8123;
        set $upstream_proto http;
        proxy_pass http://192.168.31.5:8123;

        proxy_set_header Host $host;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
# enable subdomain method reverse proxy confs
include /config/nginx/proxy-confs/*.subdomain.conf;
# enable proxy cache for auth
proxy_cache_path cache/ keys_zone=auth_cache:10m;

homeassistant configuration.yaml -

# Configure a default setup of Home Assistant (frontend, api, etc)
default_config:

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.11.0.2

I also tried moving home-assistant into the docker network and not setting it with network_mode=host but still nothing =/

Sorry it must be frustrating and a lot can go wrong here. The problem is we don’t know if its a Home Assistant config issue or Swag issue (or both). I’m thinking more likely a Swag issue but can’t rule anything out. Can you get the proxy to work for anything else besides Home Assistant? Home Assistant is super fussy about the proxy settings. Node red is not, here’s my node red reverse proxy config for example - a lot less settings

################################################################################
### SUBDOMAIN 1 Node Red########################################################
server {
	listen 443 ssl;

	root /config/www;
	index index.html index.htm index.php;

	server_name red.MYDOMAIN.duckdns.org;
	
	include /config/nginx/ssl.conf;

	client_max_body_size 0;

	location / {
#		auth_basic "Restricted";
#		auth_basic_user_file /config/nginx/.htpasswd;
		include /config/nginx/proxy.conf;
		proxy_pass http://192.168.0.181:1880;
	}
}

Was it working in older versions of Home Assistant prior to 7.2021 or is this a new install? Prior to 7.2021 didn’t have the proxy block. Can you try and downgrade to an older home assistant version and see if it works with that?

Do you have portainer? You can diagnose a lot about the network and other things going on with Docker with that using the GUI. Delete any unused networks, containers, and images.

Try either using portainer or just execute docker exec -it swag /bin/bash to bash into the Swag container. Then try pinging your host 192.168.31.5 to see if the container can reach it. If not you have some sort of network configuration issue with your lan, machine, or DNS settings causing a problem.

If all else fails, try completely deleting swag, the old image, and the config folder. Pull a brand new Swag docker image to a blank config directory and try starting over from scratch.

Also make sure you’re clearing the browser cache after anything you try changing.

Thanks! This helped me a lot! It’s working again

Hi,
I’d like to get VPN access into my system which uses SWAG/NGINX and Duckdns.
Can anyone point me to a ( wireguard ?) setup help, my internet search skills aren’t very good so I keep getting the add-on guides and not a guide for docker container.

It seems to be possible to run a reverse proxy and VPN together, but there are limited instructions. This guide is pretty bare bones and not home assistant specific, but can at least give you an idea of the nginx config. You would use swag and wireguard containers in docker Securing SWAG · GitHub

While VPN and reverse proxy together would be very secure, I think most people go with one or the other. It seems like it would be difficult to get home assistant working through all these layers of security, and I don’t see any posts with examples of a successful vpn and reverse proxy setup together in the forum. Hopefully you can get it working and let us know how it went.

Update - @Bry I may have missed what you were trying to do initially. I installed Wireguard container and it looks promising, and use it along the reverse proxy. I wrote up a more detailed guide here which includes a link to a nice video - Wireguard Container