TLS and trust boundaries
Protect the backend path
Keep application listeners private so clients cannot bypass proxy TLS, routing, logging, or access policy.
10 minute lesson
Everything this course has built — TLS termination, routing, forwarded-header trust, access logs — assumes traffic goes through the proxy. A proxy policy is effective only when the backend is not reachable through an alternate public path. If a client can hit port 4001 directly, every rule you wrote on port 443 is a suggestion.
This is the most commonly broken assumption in real deployments. An app gets bound to 0.0.0.0 during debugging, a cloud firewall rule opens “temporarily”, and the backend quietly serves unencrypted, unlogged traffic to anyone who scans for it.
Check what the backend exposes
Bind locally or restrict network access to the proxy. Start with what’s actually listening. Verify the backend listener:
ss -lntp | grep 4001
# macOS: lsof -nP -iTCP:4001 -sTCP:LISTEN
Read the local address column carefully:
LISTEN 0 511 127.0.0.1:4001 0.0.0.0:*
127.0.0.1:4001 means loopback only — unreachable from any other machine. If you see 0.0.0.0:4001 or *:4001, the backend accepts connections on every interface, and the only thing between it and the network is a firewall you may or may not have.
Our lab backend passes because backend.mjs binds explicitly: .listen(port, '127.0.0.1'). Delete that second argument and re-run ss to see the difference. Node, like most runtimes, defaults to all interfaces.
Test from the outside
A listener check tells you intent. An external probe tells you truth. From an authorized second device on the same network:
curl --max-time 3 http://192.168.1.20:4001/
# curl: (28) Connection timed out <- what you want
curl --max-time 3 http://192.168.1.20:8080/
# proxy answers <- also what you want
The backend port should be unreachable while the proxy remains available. Only probe machines you own or administer.
Layer the controls
When backends must bind beyond loopback — separate hosts, containers — binding alone isn’t enough, and obscurity is worth nothing. A hidden port is not protection: scanners find nonstandard ports in minutes. Use explicit binding and firewall rules together:
sudo ufw allow from 192.168.1.10 to any port 4001 proto tcp
sudo ufw deny 4001/tcp
That allows the proxy host (192.168.1.10 here) and rejects everyone else, even if the bind address is wide. Two layers, so one mistake doesn’t become an exposure.
Lesson completed