9router treats local loopback requests as trusted and allows access to /v1/* without an
API key. In a documented/common reverse-proxy deployment where nginx forwards public
traffic to the backend via 127.0.0.1, external non-Origin requests are misclassified as
local. This allows unauthenticated access to /v1 APIs such as /v1/models, and may allow
abuse of configured upstream provider credentials depending on the enabled providers.
v0.4.80 @ b282f05.127.0.0.1 / localhost. This mirrors the documented cloud
deployment (proxy_pass http://localhost:20128 with X-Real-IP / X-Forwarded-For).direct-backend, port 18081) returns 401 for /v1/models
without an API key.X-9r-Real-IP: 127.0.0.1 still returns 401: the
custom server deletes the client-supplied header and overwrites it with the real socket
address, so naive header spoofing does not work against the direct backend.reverse-proxy, port 18080) returns 200 with the full model
catalog for the same /v1/models request without any API key.Origin header returns 401. The bypass
therefore primarily affects curl / SDK / server-side / non-browser clients, which do
not send Origin.127.0.0.1,
the backend stamps a loopback client address for every internet client and treats the
request as local, skipping the /v1 API-key requirement. The forwarded X-Real-IP /
X-Forwarded-For headers that carry the true client IP are ignored for this decision.This repository is a self-contained Docker Compose reproduction. No real provider is called and no real API key is required.
docker compose up --build
curl -i http://127.0.0.1:18081/v1/models
curl -i -H "X-9r-Real-IP: 127.0.0.1" http://127.0.0.1:18081/v1/models
curl -i http://127.0.0.1:18080/v1/models
Origin control:
curl -i -H "Origin: http://evil.example" http://127.0.0.1:18080/v1/models
| Request | Result |
|---|---|
Direct 18081, no key |
401 Unauthorized ({"error":"API key required for remote API access"}) |
Direct 18081, X-9r-Real-IP: 127.0.0.1 spoof |
401 Unauthorized |
Proxied 18080, no key |
200 OK with the full model catalog |
Proxied 18080, with Origin |
401 Unauthorized |
/v1 API surface in the affected reverse-proxy deployment./v1/models./v1/chat/completions and other /v1 proxy endpoints (the attacker spends the operator's
provider quota/keys without holding any key of their own)./v1/* on public listeners.X-Forwarded-For chain), and never treat all
loopback proxy traffic as end-user-local.{
"cwe_ids": [
"CWE-287",
"CWE-290",
"CWE-306",
"CWE-441"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-23T18:12:26Z",
"nvd_published_at": "2026-07-10T17:17:01Z",
"severity": "HIGH"
}