Sharing port 443 between a real website and a VLESS+Reality tunnel
Table of contents
The traditional shape of a hidden service is: pick a port that is not 22, 80, or 443, hope nobody scans it, panic when somebody does. The modern shape is more honest. Live on 443. Pretend nothing. Be something.
This post is about the second shape. Specifically, about how one VPS on one IP can serve a real public website on port 443 and a VLESS+Reality tunnel on port 443, on the same TCP socket, indistinguishable to DPI because both of them are real.
What Reality actually does#
REALITY is an Xray protocol that, on the wire, looks exactly like a TLS 1.3 handshake to a real public website. Not "looks like" in the marketing sense. It uses the real ServerHello, the real certificate, the real ALPN, of an actual website that already exists on the public internet.
The client picks a decoy domain at config time. Say www.example-bank.com, picked because it is a TLS 1.3 site with HTTP/2 and a stable certificate chain. The client dials your VPS. Your VPS, running Xray, receives the ClientHello and looks at the SNI. If the SNI is www.example-bank.com, Xray opens a connection from your VPS to the real www.example-bank.com:443, splices the two together for the duration of the handshake, and the real bank's TLS stack performs the handshake the client sees.
After the handshake, Xray injects an authentication step inside the TLS session. If the client is one of yours (knows the private key for the X25519 keypair you generated), Xray hijacks the post-handshake stream and tunnels VLESS through it. If the client is not yours, Xray completes the proxy connection to the real bank, and the client sees the real bank's website.
This is the part that makes it different from a thousand other obfuscation protocols. A censor's active probe gets the real website. The certificate is the bank's. The HTML is the bank's. The behaviour under repeated probes is the bank's. There is no honeypot to detect, because there is no honeypot to begin with.
The two-tenant trick#
REALITY needs a decoy domain that is not the VPS itself. The VPS still has a real website you want to host. Both want port 443. They cannot both have it.
The solution is nginx stream with ssl_preread.
65.21.53.234:443
|
nginx stream + ssl_preread
|
SNI = decoy domain SNI = archworks.co
| |
v v
127.0.0.1:4443 127.0.0.1:8443
(Xray VLESS+Reality) (nginx HTTPS, your real site)
|
v
Internet
ssl_preread lets nginx read the SNI extension out of the ClientHello without terminating TLS. The TLS handshake has not happened yet. Only the SNI is visible. Nginx routes the entire TCP connection to whichever backend matches.
ClientHello with SNI matching the decoy goes to Xray, which does the Reality handshake and either tunnels or proxies to the real decoy. ClientHello with SNI matching the real site goes to a normal nginx HTTPS vhost on 127.0.0.1:8443. Both work. Both look correct to a passive observer. Both look correct to an active probe. The same IP serves both. There is no secret port, no hidden service, no rate-limited login page that tells a scanner there is something here.
What it does not survive#
DPI is getting cleverer. Three attacks work on Reality as of 2026.
Handshake replay. A censor records your ClientHello, replays it from a different source, observes the response. If the response from Xray differs from the response from the real decoy in any timing or framing detail, and Go's TLS stack is not byte-identical to the decoy's stack even when proxying, the replay distinguishes you. Reported in net4people/bbs issue 576. Xray patched it. The same shape will reappear.
Dual-role behavioural fingerprint. A single IP that acts both as a TLS server and as a TLS client to the same destination in a tight time window. Reality on your VPS receives a connection (server) and then opens a connection to the decoy (client). Both legs are TLS. The pattern is rare on a normal VPS. Recent academic work reports 23% recall at 0.18% false positive on this signature alone. Not a deployable detection yet. On the path.
CIDR whitelisting. Russian mobile operators only allow connections to whitelisted IP ranges. Your foreign VPS is not in those ranges, so the censor does not need to inspect anything. The packets do not leave the operator's network. Reality is bypassed by not being on the bypassable internet at all.
The first two need protocol-level fixes that the Xray team is shipping. The third is unwinnable inside the censor's network and requires a hop inside the censored region.
The CDN fallback#
For when Reality is blocked, the second-tier setup runs VLESS over WebSocket over TLS, fronted by Cloudflare.
Client -> Cloudflare anycast (any of thousands of IPs) -> your VPS on 443 -> Xray
DPI sees HTTPS to a Cloudflare IP. Blocking Cloudflare would break half the public internet, so Cloudflare IPs are universally whitelisted. The latency cost is 40-80 ms extra. The throughput is fine for browsing, painful for video. Reported uptime in active blocking events is months.
You pay for the fallback in a small ws.archworks.co subdomain proxied by Cloudflare, an Origin Certificate from Cloudflare to your VPS, and one extra inbound on Xray on 127.0.0.1:10086 for the WebSocket path.
What I learned setting this up#
Pick the decoy carefully. TLS 1.3, HTTP/2, no compression shenanigans on the ServerHello, not on someone's high-value sensitive list. RealiTLScanner does the validation. I went through five candidates before finding one that scored cleanly. Common picks like www.microsoft.com have side effects you do not want.
Move your real HTTPS off 443 first. Order of operations matters. Take 443 with nginx-stream before your real vhost has moved to 127.0.0.1:8443, and you serve nothing on 443 for a few seconds. Enough to alarm uptime monitoring. Move the vhost, reload, then change the listener.
The Cloudflare ECH knob. Cloudflare enables ECH (Encrypted Client Hello) by default for proxied domains. ECH triggers active blocking in some censorship regions even when the rest of the setup is clean. The free-tier dashboard does not let you disable ECH. The API does. Disable it before you point your fallback at it.
BBR is not optional. TCP BBR significantly improves throughput over lossy or DPI-shaped links. Enable it once on the VPS and forget about it. Default cubic is up to 40% slower in adverse conditions.
The combined setup is a single IP, a single port, a real public website, a tunneled outbound that survives most DPI, and a CDN-fronted fallback that survives the rest. A lot of moving parts. Each part single-purpose, well-documented, replaceable. The thing that made it click for me was the realisation that the decoy is not a pretend website. It is a real third-party website doing its real job, and your TLS handshake is exactly its TLS handshake, because it actually is.
The detection-side post is coming. Every defender's tool has a corresponding signal. The arms race is real and it is technical and it is exhausting, and that is the point.