Skip to content

Instantly share code, notes, and snippets.

@vidia
Last active March 22, 2026 15:23
Show Gist options
  • Select an option

  • Save vidia/fbef2ee643b23848d8b24211d5860b78 to your computer and use it in GitHub Desktop.

Select an option

Save vidia/fbef2ee643b23848d8b24211d5860b78 to your computer and use it in GitHub Desktop.
Example, working, NGINX config for proxying to Unifi Controller software and using letsencrypt. Includes websocket fix.
# I had a bit of trouble getting my unifi controller (hosted offsite) to use a proxy/letsencrypt. So here are the fruits of my labor.
# The unifi default port is 8443 running on localhost.
# License: CC0 (Public Domain)
server {
# SSL configuration
#
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name unifi.hostname.com;
# Needed to allow the websockets to forward well.
# Information adopted from here: https://community.ubnt.com/t5/EdgeMAX/Access-Edgemax-gui-via-nginx-reverse-proxy-websocket-problem/td-p/1544354
location /wss/ {
proxy_pass https://localhost:8443;
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_read_timeout 86400;
}
location / {
proxy_pass https://localhost:8443/; # The Unifi Controller Port
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
}
# Unifi still internally uses its own cert. This was converted to PEM and
# is trusted for the sake of this proxy. See here for details:
# https://community.ubnt.com/t5/UniFi-Wireless/Lets-Encrypt-and-UniFi-controller/td-p/1406670
ssl_trusted_certificate /etc/nginx/ssl/unifi/unifi-default-selfsign.pem;
ssl_certificate /etc/letsencrypt/live/unifi.hostname.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/unifi.hostname.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
}
server {
listen 80;
listen [::]:80;
server_name unifi.hostname.com;
location / {
return 301 https://$host$request_uri;
}
}
@bluenevus

Copy link
Copy Markdown

thank you thank you thank you!!!!

@jimmarks

Copy link
Copy Markdown

This worked for me with one tweak. I am using a stand-alone nginx server for multiple sites. I ended up changing the proxy_pass lines to direct them to my actual internal IP Address.

The rest of this setup worked amazingly.

THANK YOU!!!

ghost commented Oct 16, 2019

Copy link
Copy Markdown

works a fuckin' treat. thx a ton.

@frankhommers

Copy link
Copy Markdown

This does not work for inform-urls.

@roinou

roinou commented Feb 3, 2020

Copy link
Copy Markdown

Mine was working without the wss part, I'll see with time if it improves or not.
Nevertheless, I still get HTTP 413 when uploading large backups (>1Mb). It seems to be nginx, as there are no errors on the unifi part. Adding this does the trick: client_max_body_size 10M;

@Ramblurr

Ramblurr commented Mar 3, 2020

Copy link
Copy Markdown

The https is very important the proxy_pass https://localhost:8443/ line. I was copy/pasting from an old config and kept getting
the error: This combination of host and port requires TLS. I had forgotten to change the http -> https.

@robwolff3

Copy link
Copy Markdown

After a bunch of trial and error the configuration below is finally how I got the web socket errors to stop. Its a hybrid of my own, @vidia and @tongphe I have the server block repeated twice on different ports for my guest portal which I'm also proxying through nginx.

server {
        listen 443 ssl http2;
        server_name unifi.domain.com;
        include /config/nginx/ssl.conf;
        ssl_trusted_certificate /config/nginx/unifi.pem;
        location /ws {
                proxy_pass https://192.168.xxx.xxx:8443;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "Upgrade";
	        proxy_set_header Host $http_host;
        }
        location / {
	        proxy_pass https://192.168.xxx.xxx:8443/;
	        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	        proxy_set_header Host $http_host;
	        proxy_set_header X-Forwarded-Proto $scheme;
	        proxy_set_header X-Real-IP $remote_addr;
	        proxy_set_header Upgrade $http_upgrade;
	        proxy_set_header Connection "Upgrade";
	        proxy_buffering off;
	        proxy_ssl_verify off;
        }
}
server {
        listen 8843 ssl http2;
        server_name unifi.domain.com;
        include /config/nginx/ssl.conf;
        ssl_trusted_certificate /config/nginx/unifi.pem;
        location /ws {
                proxy_pass https://192.168.xxx.xxx:8844;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "Upgrade";
	        proxy_set_header Host $http_host;
        }
        location / {
	        proxy_pass https://192.168.xxx.xxx:8844/;
	        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	        proxy_set_header Host $http_host;
	        proxy_set_header X-Forwarded-Proto $scheme;
	        proxy_set_header X-Real-IP $remote_addr;
	        proxy_set_header Upgrade $http_upgrade;
	        proxy_set_header Connection "Upgrade";
	        proxy_buffering off;
	        proxy_ssl_verify off;
        }
}

@blitzkrieggold

Copy link
Copy Markdown

I took the original and added this line to the location /wss/ section and it seems to work.
proxy_set_header Origin '';

@DomenicoCasillo

Copy link
Copy Markdown

I took the original and added this line to the location /wss/ section and it seems to work.
proxy_set_header Origin '';

The above is perfectly fine.

@rexkani

rexkani commented May 28, 2020

Copy link
Copy Markdown

Hi guys,

may i know if this code works for AP adoption "inform"?

@azN2

azN2 commented May 31, 2020

Copy link
Copy Markdown

Hi guys,

may i know if this code works for AP adoption "inform"?

https://domain/inform

what is everyone doing about the STUN warning, have you been able to solve this?

@vidia

vidia commented Jun 1, 2020

Copy link
Copy Markdown
Author

Thank you to everyone who has responded to this. I will have to go through all of these suggestions and take in whatever new changes are working best for people. I have the posted code deployed currently and haven't noticed any issues, but it is possible I've missed something because I haven't updated it since I posted it initially.

@azN2, I do have STUN issues on my box, but my issues seem to be because STUN is binding to ipv6 and not ipv4, which is causing it to not be accessible over ip4 like I want it to be. I haven't found a solution to force it to use ipv4, but I'm mentioning it here in case that bit of info helps you find your own solution.

@rexkani, I believe this will work for /inform. I don't believe that will be anything special or different than accessing the controller regularly.

@rexkani

rexkani commented Jun 2, 2020

Copy link
Copy Markdown

hi @azN2 and @vidia, the code did not work for STUN and inform, these traffics does not run on HTTPS and therefore needs to make use of the nginx "Stream" module. i've leart that from below discussion:

another thing that is still missing after adding the stream for STUN and inform is the remote guest captive portal. i seems to be running on 8880? i dont have time to work on it yet.

@systemofapwne

systemofapwne commented Jun 18, 2020

Copy link
Copy Markdown

Thank you. You directed me into the right direction. Here is "my snippet", which basically enables websockets & httpupgrade on / (catchall) instead of splitting between / and /wss. The reason is, that more locations than just /wss need http upgrade

server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;
	
	server_name MY.DOMAIN;
	
	#SSL/TLS config
	include /PATH/TO/SSL.CONF;
	
	location / {
	        #Unifi ip
	        proxy_pass https://10.1.0.106:8443/;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
                proxy_ssl_verify off; # Ignores the unifi controller's self-signed certificate (potentially allows MITM - Add the certificates, as shown for the other examples above, in order to mitigate this attack vector)
        
        	#"Upgrade" for WebSockets
                proxy_http_version 1.1;
                proxy_buffering off;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "Upgrade";
                proxy_read_timeout 86400;
        }
}

As a little sidenote: If you plan to use HTTP Auth, you need to block forwarding of the auth-data to the unify-controller. Otherwise you will end up with a 400: Bad Request error.

Simply add this line to the location block

proxy_set_header Authorization "";

@MaximvanLuttikhuizen

MaximvanLuttikhuizen commented Aug 7, 2020

Copy link
Copy Markdown

I took the original and added this line to the location /wss/ section and it seems to work.
proxy_set_header Origin '';

This also worked for me. The errors in the user interface disappeared. The return code in Nginx changed from 404 to 101 (Switching protocol).

@filupmarley

Copy link
Copy Markdown

The https is very important the proxy_pass https://localhost:8443/ line. I was copy/pasting from an old config and kept getting
the error: This combination of host and port requires TLS. I had forgotten to change the http -> https.

This was my issue as well. This fixed it for me!

@jleboube

Copy link
Copy Markdown

I realize this is for self hosted controller, however, can anyone let me know if you've been able to get nginx reverse proxy working with a Cloud Key Gen 2 Plus? I am able to get to a login screen, but when UniFi attempts to perform the two factor auth, it bombs out. Any help would be appreciated.

@frankosborne

Copy link
Copy Markdown

Does anyone have a good example of doing this but with a location like /unify/? I can run it from root fine but once I change the location. I cant get it to work :(

@ftc2

ftc2 commented May 4, 2021

Copy link
Copy Markdown

@frankosborne

Does anyone have a good example of doing this but with a location like /unify/? I can run it from root fine but once I change the location. I cant get it to work :(

i believe it is possible to do, but i don't think it's a good idea unless they add a setting in the controller to support that. otherwise it's too much of a kludge. upvote this maybe? https://community.ui.com/questions/Request-Add-a-controller-config-option-for-http-path-prefix/416eb730-d01d-4de5-b336-381091e01c0c

@ftc2

ftc2 commented May 4, 2021

Copy link
Copy Markdown

here is my config in case anyone wants another example:

##
# Virtual Host configuration for unifi.example.com
##

upstream unifi {
    server localhost:8443;
    keepalive 64;
}

# http:// --> https://
server {
    listen 80;
    # if you need IPv6:
#     listen [::]:80;
    server_name unifi.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    # if you need IPv6:
#     listen [::]:443 ssl http2;
    server_name unifi.example.com;

    http2_push_preload on; # Enable HTTP/2 Server Push

    ssl on;
    ssl_certificate /etc/letsencrypt/live/unifi.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/unifi.example.com/privkey.pem;
    ssl_session_timeout 1d;

    # Enable TLS versions (TLSv1.3 is required upcoming HTTP/3 QUIC).
    ssl_protocols TLSv1.2 TLSv1.3;

    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';

    ssl_prefer_server_ciphers on;
    # https://stackoverflow.com/questions/37262154/can-all-nginx-vhosts-share-the-same-ssl-session-cache
    ssl_session_cache shared:SSL:50m;
    add_header Strict-Transport-Security max-age=15768000;
    ssl_stapling on;
    ssl_stapling_verify on;

    location / {
        proxy_pass https://unifi;
        # i thought client_max_body_size would be necessary, but apparently not. i downloaded a 75MiB .unf backup from the controller np.
        # it MIGHT be needed for uploading a large file (restoring a backup), but i haven't tested yet.
#        client_max_body_size 100m;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffers 256 16k;
        proxy_buffer_size 16k;
        proxy_read_timeout 600s;
        proxy_http_version 1.1;
    }
}

@asanka-chanaka

Copy link
Copy Markdown

I'm a noob working with Nginx.
My nginx is setup on docker - i can't seem to find how the " ssl_certificate" and "ssl_certificate_key" paths.
Can someone direct me towards where these values can be found and where above conf need to be added?

@systemofapwne

Copy link
Copy Markdown

I'm a noob working with Nginx.
My nginx is setup on docker - i can't seem to find how the " ssl_certificate" and "ssl_certificate_key" paths.
Can someone direct me towards where these values can be found and where above conf need to be added?

This is more an nginx and docker related question.
However, you still need basically the nginx-config for having ssl to be enabled

    ssl on;
    ssl_certificate /container-path/to/cert.pem;
    ssl_certificate_key /container-path/to/key.pem;

and then you need to e.g. bind-mount the certs/keys files from your host to your docker container.
Lets assume, they live on /host-path/to/cert.pem and /host-path/to/key.pem. If you use docker-compose, an except of the config might look like this

    volumes:
      - '/host-path/to/cert.pem:/container-path/to/cert.pem:ro'
      - '/host-path/to/key.pem:/container-path/to/key.pem:ro'

The ro tag is there to make the files read-only in the container.
Adjust for your usecase (bare-docker, k8, rancher, portainer etc accordingly).

@sinbrkatetete

sinbrkatetete commented May 10, 2021

Copy link
Copy Markdown

I'm a noob working with Nginx.
My nginx is setup on docker - i can't seem to find how the " ssl_certificate" and "ssl_certificate_key" paths.
Can someone direct me towards where these values can be found and where above conf need to be added?

If you're using SWAG or a similar docker (nginx with auto letsencrypt/certbot), there's a chance you don't need to explicitly set ssl certs and/or ssl, just include the ssl.conf in this config.
Something like:
include /config/nginx/ssl.conf

Please take this with a huge grain of salt (a mountain of it) as I'm a total noob who's just looking around at how to proxy non http(s) traffic to unifi controler so as to adopt a USG remotely and proxy all the ports/traffic through nginx as securely as possible. I HAVE MOSTLY NO CLUE WHAT I'M TALKING ABOUT!

@asanka-chanaka

Copy link
Copy Markdown

and then you need to e.g. bind-mount the certs/keys files from your host to your docker container.
Lets assume, they live on /host-path/to/cert.pem and /host-path/to/key.pem. If you use docker-compose, an except of the config might look like this

    volumes:
      - '/host-path/to/cert.pem:/container-path/to/cert.pem:ro'
      - '/host-path/to/key.pem:/container-path/to/key.pem:ro'

Thanks for the reply. I'll give this a try.

@asanka-chanaka

Copy link
Copy Markdown

I HAVE MOSTLY NO CLUE WHAT I'M TALKING ABOUT!

Welcome to the club :D

@mbnn

mbnn commented Jul 13, 2021

Copy link
Copy Markdown

I took the original and added this line to the location /wss/ section and it seems to work.
proxy_set_header Origin '';

This also worked for me. The errors in the user interface disappeared. The return code in Nginx changed from 404 to 101 (Switching protocol).

This solved it also for me :) Thx!

@Max101

Max101 commented Dec 19, 2021

Copy link
Copy Markdown

Hi, I found myself wanting to do this for putting the Unifi controller inside an iFrame to use with HomeAssistant.

To add it into the iFrame you need to set a few additional headers into both the location /wss/ and location / parts of the nginx conf.

Here they are:

# Headers required for unifi to work
proxy_hide_header X-Frame-Options;
proxy_hide_header X-XSS-Protection;
proxy_hide_header X-Content-Type-Options;
proxy_cookie_path / "/; secure; SameSite=none";
add_header X-Frame-Options "ALLOWALL";

@manustars

Copy link
Copy Markdown

hi to all, i configure them, but open a white paga without any error.
location /wss/ {
proxy_pass https://192.168.1.5:8443;
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_read_timeout 86400;
}
location / {
proxy_pass https://192.168.1.5:8443;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_http_version 1.1;
proxy_set_header Connection "upgrade";

}

@gthb

gthb commented Jul 30, 2025

Copy link
Copy Markdown

Shouldn't that be X-Forwarded-For?

-                proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
+                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

@franz82

franz82 commented Mar 22, 2026

Copy link
Copy Markdown

Hi,
thanks for the config.
I slightly modified /wss section like this:

        location /wss/ {
                proxy_pass https://localhost:8443/wss/;
                proxy_http_version 1.1;
                proxy_buffering off;
                proxy_set_header Host $host;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "Upgrade";
                proxy_read_timeout 86400;
        }

otherwise Unifi backup config download link (Settings > System > Backups > Download) didn't work.
Tested on Unifi Controller v9.5.21

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment