How to configure an NGINX Reverse Proxy through a Firewall

A Haiku

VPS forwards \n Traffic through reverse proxy \n Towards homeserver

Ponder over what this haiku means. Truly, become IT. Only then may you move on to the next step.

What we need to accomplish

sudo apt install nginx
sudo systemctlm start service.nginx

Or something like that. Now, we configure it.— in this case, NGINX will reverse proxy (aka serve as a proxy for an anonymous server, in our case a homeserver only accessible through wireguard). First, assuming you already know how to configure NGINX see my previous blog post, the following config file is necessary for your home server to receive traffic.

server {
        listen 3456;
        server_name www.your-domain.here your-domain.here;
        location / {
                proxy_pass http://localhost:app_port; # the right address:port combination if you need
                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;
                }
        }

We can now access our app locally via http://localhost:app_port, and externally (in theory) through port 3456 (or whatever VPN port you chose). After setting up both NGINX files (and configuring WireGuard to communicate through port 3456 or whatever you chose), the VPS will receive requests to yourdomain.com from your DNS of choice then forward them over to your home server. Below is the config you need to for your VPS to serve your app over HTTPS:

server {
        listen 443 ssl;
        server_name www.your-domain.here your-domain.here;
        ssl_certificate /.ssh/id_rsa.pub;
        ssl_certificate_key /.ssh/id_rsa;
        location / {
                proxy_pass http://10.0.0.2:3456; # Replace with your backend application's address and port. No need to change if you followed my guide on VPN setup.
                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;
                }
        }

Both afraid.org and Cloudflare have web APIs that can generate SSL certificates, so I’m not going to go over those details.

server {
        listen 443 ssl;
        server_name www.your-domain.here your-domain.here;
        ssl_certificate /.ssh/id_rsa.pub;
        ssl_certificate_key /.ssh/id_rsa;
        location / {
                proxy_pass http://10.0.0.2:3456; # Replace with your backend application's address and port. No need to change if you followed my guide on VPN setup.
                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;
                }
        }

Ok, so we don’t have an ssl certificate yet. There’s a few places you can get those, but I won’t get into details here, as I just used the Cloudflare Web API to create them, then I SSH’d to my VPS and stored them there. Don’t share your ssl key with anyone. Consider restricting access to it:

chmod 600 /.ssh/id_rsa
chown nginx:nginx /.ssh/id_rsa

Wait, so it receives HTTPS traffic, then it forwards it over a different port altogether through the VPN. That’s actually cool.

Setting Up the Homeserver Reverse Proxy

Your homeserver also needs NGINX to receive traffic from the VPS. Here’s the configuration:

Install NGINX on Homeserver

sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx

Create Homeserver NGINX Config

Create /etc/nginx/sites-available/app:

server {
    listen 3456;
    server_name _;
    
    location / {
        proxy_pass http://localhost:8080;  # Your app's actual port
        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_buffering off;
    }
}

Enable the Configuration

sudo ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Testing Your Setup

  1. Test locally (on homeserver):

    curl http://localhost:3456
  2. Test through VPN (from VPS):

    curl http://10.0.0.2:3456
  3. Test externally: Visit https://yourdomain.com in your browser

Complete Architecture

Here’s how traffic flows:

  1. User requests https://yourdomain.com
  2. DNS points to VPS IP
  3. VPS NGINX receives HTTPS request on port 443
  4. VPS forwards to 10.0.0.2:3456 through WireGuard
  5. Homeserver NGINX receives on port 3456
  6. Homeserver proxies to local app on port 8080
  7. Response travels back the same path

Securing Your Setup

Restrict NGINX to VPN

On your homeserver, only allow traffic from the VPN:

sudo ufw allow from 10.0.0.0/24 to any port 3456
sudo ufw deny 3456

Monitor Logs

# NGINX access logs
sudo tail -f /var/log/nginx/access.log

# NGINX error logs
sudo tail -f /var/log/nginx/error.log

Troubleshooting

502 Bad Gateway

  • Check if your app is running on the specified port
  • Verify WireGuard tunnel is active
  • Check homeserver NGINX is running

Connection Timeout

  • Verify firewall rules allow port 3456
  • Check WireGuard configuration
  • Ensure VPS can ping homeserver through VPN

SSL Errors

  • Verify certificate paths in VPS config
  • Check certificate permissions
  • Ensure certificates haven’t expired

Performance Tips

  1. Enable NGINX caching for static content
  2. Use gzip compression
  3. Implement rate limiting
  4. Consider using HTTP/2
# Add to VPS config
gzip on;
gzip_types text/plain text/css application/json application/javascript;

client_max_body_size 10M;
proxy_read_timeout 300s;

Conclusion

You now have a complete reverse proxy setup that securely exposes your homeserver to the internet through a VPS, bypassing CGNAT restrictions. Your data stays on your hardware while being accessible globally.

Logo

© 2026 Martin Munguia