Nginx is a lightweight, high-performance web server and reverse proxy. As a reverse proxy, Nginx accepts requests from clients and forwards them to backend applications (e.g. Home Assistant on port 8123, Frigate on port 5000) — so everything is accessible through ports 80/443 with different domains.

Why use a Reverse Proxy? Instead of accessing a service at http://192.168.1.10:8123, you can access it at https://ha.domain.com. One public IP can serve multiple services, with centralized SSL at Nginx.

Requirements

  • OS Ubuntu / Debian
  • Root or sudo access
  • A domain pointing to the server IP (for SSL setup)
  • Ports 80 and 443 open in the firewall

Install Nginx

  1. 1

    Install Nginx from the Official Repository

    apt update
    apt install -y nginx
    
    # Make sure Nginx is running
    systemctl enable nginx
    systemctl start nginx
    systemctl status nginx

    Open a browser to http://Server-IP — if you see the "Welcome to nginx!" page, the installation was successful.

  2. 2

    Understand the Nginx Directory Structure

    /etc/nginx/
    ├── nginx.conf               # Main configuration
    ├── sites-available/         # All virtual host configs (active/inactive)
    └── sites-enabled/           # Symlinks to active sites-available configs

    Best practice: create a config per domain in sites-available/, then activate it with a symlink to sites-enabled/.

Reverse Proxy Configuration

Example: Proxy to Home Assistant

Create a new config file at /etc/nginx/sites-available/homeassistant:

server {
    listen 80;
    server_name ha.domain.com;

    location / {
        proxy_pass         http://127.0.0.1:8123;
        proxy_http_version 1.1;

        proxy_set_header Host               $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;

        # WebSocket support (required for Home Assistant)
        proxy_set_header Upgrade   $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Example: Proxy to Frigate NVR

Create file /etc/nginx/sites-available/frigate:

server {
    listen 80;
    server_name frigate.domain.com;

    # Increase upload limit for snapshots/clips
    client_max_body_size 100M;

    location / {
        proxy_pass         http://127.0.0.1:5000;
        proxy_http_version 1.1;

        proxy_set_header Host               $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_set_header Upgrade   $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Enable Virtual Hosts

# Create symlinks to enable the configs
ln -s /etc/nginx/sites-available/homeassistant /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/frigate       /etc/nginx/sites-enabled/

# Remove default config if not needed
rm /etc/nginx/sites-enabled/default

# Test configuration
nginx -t

# Reload Nginx
systemctl reload nginx
Always run nginx -t before reloading to ensure there are no syntax errors in the configuration files.

SSL Setup with Let's Encrypt (Certbot)

  1. 1

    Install Certbot

    apt install -y certbot python3-certbot-nginx
  2. 2

    Obtain an SSL Certificate

    Certbot will automatically detect the domains from the Nginx config and handle HTTP verification:

    certbot --nginx -d ha.domain.com -d frigate.domain.com

    Follow the prompts: enter your email, agree to the ToS, and choose whether to auto-redirect HTTP → HTTPS (recommended: Yes).

  3. 3

    Verify Auto-Renewal

    Certbot automatically adds a cronjob to renew certificates before they expire (90 days). Test a dry run:

    certbot renew --dry-run

After Certbot finishes, the Nginx config files will be automatically updated with SSL configuration. The result looks something like this:

server {
    listen 443 ssl;
    server_name ha.domain.com;

    ssl_certificate     /etc/letsencrypt/live/ha.domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ha.domain.com/privkey.pem;
    include             /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        proxy_pass        http://127.0.0.1:8123;
        proxy_http_version 1.1;
        proxy_set_header Host              $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_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        "upgrade";
    }
}

server {
    listen 80;
    server_name ha.domain.com;
    return 301 https://$host$request_uri;
}

Common Nginx Commands

# Test configuration (always run before reloading)
nginx -t

# Reload configuration without dropping connections
systemctl reload nginx

# Restart Nginx
systemctl restart nginx

# View access log
tail -f /var/log/nginx/access.log

# View error log
tail -f /var/log/nginx/error.log

Tip: One Config for Multiple Subdomains

If you have many services, you can create a cleaner single file using upstream blocks:

# /etc/nginx/sites-available/services

upstream homeassistant { server 127.0.0.1:8123; }
upstream frigate        { server 127.0.0.1:5000; }

server {
    listen 443 ssl;
    server_name ha.domain.com;
    # ... ssl config ...
    location / { proxy_pass http://homeassistant; }
}

server {
    listen 443 ssl;
    server_name frigate.domain.com;
    # ... ssl config ...
    location / { proxy_pass http://frigate; }
}
Firewall: Make sure ports 80 and 443 are open in UFW or iptables. With UFW: ufw allow 'Nginx Full'. If using a cloud provider (AWS, GCP, etc.), open those ports in Security Group / Firewall rules.