Proxies·8 min read·

Migrating to a Proxy-First Architecture Safely

Move a direct-to-internet app behind a reverse proxy in staged steps with minimal downtime, instant rollback, and smoke tests before cutover.

NB

Netbay Engineering

Netbay Engineering

On this page

Most servers were not born proxy-first. Somewhere in the past someone pointed a framework directly at the public internet, and since it worked, it stayed. Migrating to a reverse proxy in front of a live app feels risky — but done in stages it is one of the gentler infra changes you can make. The plan below assumes nothing breaks except your habit of reaching apps on bare ports.

Stage the migration

  1. **Put the proxy on the box, pointing at the app, on a test port.** Nothing listens on 80 yet; you are validating config, not switching traffic.
  2. **Verify with Host headers and local curl.** Prove that proxied traffic produces the same responses as direct traffic.
  3. **Move the app off the public port.** Bind the app to 127.0.0.1 only, so the proxy becomes the sole public entry point.
  4. **Expose the proxy on 80, then 443 with a certificate.**
  5. **Point DNS at the proxy and run smoke tests.**
  6. **Keep the rollback path warm:** the old direct route still configured, just not enabled.

Step by step: from direct to proxied

Start by proving the proxy behaves. With the app still on its public port, add a proxy port that talks to it — a complete side-by-side that risks zero user traffic.

nginx
server {
    listen 18080;
    server_name _;
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Then compare direct and proxied responses byte for byte before touching anything live.

bash
curl -s http://127.0.0.1:3000/health > direct.out
curl -s -H "Host: app.example.com" http://127.0.0.1:18080/health > proxied.out
diff direct.out proxied.out && echo OK

A clean diff is your green light. The moment one response differs — a missing header, a mangled absolute URL — you fix it now, while both paths still exist.

The cutover

Rebind the app to localhost, point the proxy at it, move the proxy onto 80, and add TLS. Then descend any NAT or firewall rules.

bash
sed -i 's/0.0.0.0:3000/127.0.0.1:3000/' /etc/app/app.conf
systemctl restart app
nginx -t && systemctl reload nginx
ufw allow 80/tcp
ufw allow 443/tcp
ufw delete allow 3000/tcp

The sed line tweaks whichever file actually binds the app; the order matters — close the old public port only after the proxy is answering on 80 and 443. Requests in flight for the old port fail for the duration of the reload, so schedule each step for an off-peak window.

Smoke tests and the rollback runbook

After the cutover, run the same assertions a user would and check that the proxy is really the only entrance.

bash
curl -sfI https://app.example.com/ | head -n 3
curl -s 127.0.0.1:3000/health -o /dev/null -w "%{http_code}
"   # expect 000
curl -s http://127.0.0.1:443/health -o /dev/null -w "%{http_code}
"

The middle line confirms the app no longer answers on the public port, which is the whole point. Write the rollback steps down before you start: restore the app's public bind, reload, re-open the firewall port, and remove the proxy from 80. A rollback that requires thinking is a rollback you will not perform.

Staged migration, zero long outage 1. Side-by-side proxy on test port app still public 2. Rebind app localhost only proxy sole entry 3. Cutover 80 + 443 live firewall closes 3000 At every stage both routes exist, so the previous state is one reload away. Diff responses before and smoke test after.

Takeaway

The safest migration is a reversible one: proxy beside the app, diff the responses, rebind the app to localhost, cut ports over, and leave the rollback runbook written down. Done this way, the proxy-first change is boring on purpose. A fresh Netbay VPS is the ideal rehearsal box — run the whole sequence once for practice, then again in production, at netbayhosts.in.

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