Proxies·8 min read·

Nginx Reverse Proxy Setup from Scratch (Server Blocks)

A step-by-step Nginx reverse proxy from a clean VPS: install, server blocks, proxy_pass, upstreams, and the header set you need for real client IPs.

NB

Netbay Engineering

Netbay Engineering

On this page

Nginx is the most common reverse proxy on the internet, and for good reason: it has a small memory footprint, runs happily on a single VPS core, and its configuration model is easy to reason about once you understand server blocks. A server block is just a named chunk of config that says: for this Host header, do these things. This post walks from a fresh install to a proxy that fronts two different apps.

Install Nginx

On any current Ubuntu LTS the package manager already carries a recent Nginx. Install it, note the version, and confirm the default site is gone before you add your own config.

bash
apt update && apt install -y nginx
nginx -v
rm -f /etc/nginx/sites-enabled/default
systemctl enable --now nginx

Nginx loads every file in sites-enabled. The normal workflow is to author a config in sites-available and symlink it into sites-enabled, which makes enabling and disabling a site a one-command operation instead of an edit.

Anatomy of a server block

A minimal reverse proxy server block has four responsibilities: listen, match a name, point at something with proxy_pass, and pass the headers the upstream needs.

nginx
server {
    listen 80;
    listen [::]:80;
    server_name api.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}

The listen lines bind port 80 on IPv4 and IPv6. server_name is the Host header, not an IP address, that triggers this block. Inside the location, what looks like a URL is actually a URL prefix pattern; with the slash it matches every path. proxy_pass rewrites nothing by default, so /health from the client lands on the upstream as /health.

Proxy to two backends, not one

The moment you run two apps you want an upstream block, which gives you a named group plus simple round-robin when the app is later scaled to two copies.

nginx
upstream app_green {
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    keepalive 16;
}

server {
    listen 80;
    server_name green.example.com;

    location / {
        proxy_pass http://app_green;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Two details matter here. proxy_http_version 1.1 with an emptied Connection header enables keepalives to the upstream, which reuses sockets instead of reopening them per request. And proxy_pass now names the upstream group rather than a literal address, so Nginx will round-robin across 3001 and 3002. Add one more server line to scale; remove one to take a copy out of rotation without touching the app.

Enable, test, reload

Nginx syntax errors are the classic way to take a whole site down, so make -t part of your workflow.

bash
cat > /etc/nginx/sites-available/green <<'EOF'
server {
    listen 80;
    server_name green.example.com;
    location / {
        proxy_pass http://app_green;
    }
}
EOF
ln -s /etc/nginx/sites-available/green /etc/nginx/sites-enabled/green
nginx -t && systemctl reload nginx
curl -I http://green.example.com/health

The test step validates the whole merged config, not just this file. If it passes, reload — not restart — swaps config in place without dropping active connections. Then the curl line confirms the proxy answers and returns the upstream's status code.

One Nginx, two server blocks, two apps api.example.com /v1 requests app.example.com browser traffic Nginx proxy server blocks match Host header route to upstream API app 127.0.0.1:3000 Web app 127.0.0.1:8080

Takeaway

Server blocks give you a clean, reversible way to front every app on a box with one proxy process. Install, author a block in sites-available, symlink it, test with nginx -t, and reload. From there scaling is just another server line in an upstream group. You can rent a small compute instance on netbayhosts.in, deploy Nginx, and have this running in the time it takes to read the config once more.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance