NGINX in front of Xray
Why the classic "user → nginx → grpc_pass xray" deployment exists, what NGINX is actually doing there, and when you can delete it.
Half the “how do I set up VLESS” guides end with a diagram that has an NGINX box in the middle: user → nginx → grpc_pass → xray. It looks like ceremony, but that NGINX is doing three
specific jobs that the pre-REALITY transports could not do for themselves — and once you know
which three, you know exactly when you can delete it. Read this alongside XHTTP and Self-steal; it’s the same question from
the fronting side.
NGINX did three jobs the old transports couldn’t
Before REALITY, a VLESS-over-TLS-over-gRPC (or WebSocket) server needed a real certificate, a believable website, and a way to route only the secret path to the proxy — and Xray owned none of those, so NGINX supplied all three.
- Terminate TLS with a real cert. The transport was plain TLS with a Let’s Encrypt cert for a
domain you own; NGINX held the cert and the private key on
:443. - Serve a real site as camouflage. Anyone (a censor, a curious visitor) who opened
https://your-domain/had to get a genuine web page, or the host was obviously “just a proxy.” NGINX served that page for every path except the secret one. - Route the proxy path to the backend. For gRPC,
grpc_passforwarded a specific HTTP/2 service to Xray listening on localhost; for WebSocket/XHTTP,proxy_passwith theUpgradeheaders did the same for a secret path. Everything else stayed on the website.
server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
location / { root /var/www/site; } # real page for browsers
location /your-grpc-service { # only the secret path
grpc_pass grpc://127.0.0.1:2053; # → Xray gRPC inbound
}
} REALITY collapses all three into Xray
REALITY was designed to make that NGINX unnecessary, because it does the same three jobs
itself. It terminates TLS using a borrowed certificate (so there’s no cert to obtain or serve),
and Xray’s VLESS inbound has a fallbacks mechanism that routes anything which isn’t a valid
proxy request — by SNI, ALPN, and request path — to a dest, exactly the triage location blocks did.
The inbound lowercases the real ServerName from the connection, longest-substring-matches it
against your fallbacks, then narrows by alpn and path before dialing the fallback — a small
web-router built into the proxy. So for a single cover domain, the NGINX box has nothing left to
do that Xray isn’t already doing.
Keep NGINX only when the box is genuinely a web host
Delete NGINX for a single-purpose REALITY proxy; keep it when you actually serve web content or need it to route more than the proxy.
NGINX in front of a single REALITY proxy
Terminating TLS in NGINX and reverse-proxying to a REALITY inbound means two TLS stacks and a redundant cert for one cover domain that REALITY mirrors by itself. It adds a moving part and a second fingerprint for no gain.
NGINX when it earns its keep
Keep it when the host runs multiple virtual hosts, serves a real site you want indexed and logged,
shares :443 across several backends, or fronts a plain-TLS XHTTP/gRPC/WS transport where a real
cert and a real page are the whole disguise. Then Xray is one location/grpc_pass among many.
The tell: if the thing in front of Xray would serve more than your one cover domain — real pages, real logs, several backends — you want a web server. If it exists only to hold a cert and echo one decoy page, REALITY already is that web server.