9Router: evaluate an AI gateway without assuming unlimited access
Read 9Router’s forwarding-header trust rule
Inspect loopback detection, internal-header replacement and why launching the wrong entry point changes the request boundary.
What you will learn
- Use the socket as the starting point
- Replace internal metadata
- Test the deployment path
Before you start
- Basic command-line and configuration reading
- Ability to work in a disposable authorized environment
Design an evidence record that separates requested model, actual attempts and accepted response without retaining secrets.
Key takeaways
- Forwarded IP is trusted only on the loopback branch.
- The via-proxy marker is not the complete trust decision.
- A different startup path can omit the wrapper.
Use the socket as the starting point
The wrapper reads req.socket.remoteAddress and considers 127.0.0.1, ::1 and ::ffff:127.0.0.1 loopback proxies. For those peers only, it can use x-real-ip or the first comma-separated x-forwarded-for entry as the derived address.
For a direct non-loopback peer, a supplied forwarding address does not replace the socket address in this branch. That prevents this particular derivation from trusting an arbitrary remote caller’s claimed client IP.
Replace internal metadata
Before invoking the application, the wrapper deletes incoming x-9r-real-ip, x-forwarded-for, x-9r-via-proxy and x-9r-peer-token. It then stamps a derived IP and a per-process random peer token. The original presence of forwarding headers controls a separate via-proxy marker.
Do not interpret that marker alone as proof that the forwarding address was trusted. The trust decision also checks loopback. The wrapper does not establish that every downstream consumer uses these fields correctly; those consumers need their own review.
Test the deployment path
A narrow test plan covers a direct remote peer with forged forwarding data, a loopback proxy with clean headers, no forwarding headers and a container-network proxy. Check derived fields and removed inputs independently.
The file comments state that bare Next startup does not load this wrapper. Test through the actual production entry point and proxy topology. We did not execute these fixtures or audit the separate h2c upgrade path.
Decision guide
| Criterion | Option A | Option B |
|---|---|---|
| Best when | You need predictable behavior and easy auditing | You need adaptive optimization and have reliable telemetry |
| Main risk | May leave performance on the table | Can become difficult to explain or debug |
Implementation steps
- 1
Read socket and loopback conditions before header precedence.
- 2
Check replacement of client-supplied internal fields.
- 3
Test through the custom entry point and real proxy topology.
Copy-ready example
{
"proposedFixture": true,
"peer": "203.0.113.10",
"forwardedFor": "198.51.100.20",
"expectedDerivedIp": "203.0.113.10",
"executed": false
}Frequently asked questions
Does any x-forwarded-for determine identity?
Not in this wrapper; non-loopback peers retain their socket address.
Is the entire gateway security-verified?
No. This is inspection of one request boundary without executed integration tests.
Sources
- 9Router / custom-server.jsSource checked 2026-09-18