mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-28 00:24:19 +00:00
d882d6aa74
Surface the existing sockopt knobs (acceptProxyProtocol, trustedXForwardedFor) as a guided 'Real client IP' preset selector in the inbound form, so the real visitor IP is recovered behind Cloudflare CDN or an L4 tunnel/relay instead of recording the intermediary address. Presets are mutually exclusive, warn on incompatible transports, and add tooltips, docs, and translations for all locales.
103 lines
4.8 KiB
Markdown
103 lines
4.8 KiB
Markdown
# Capturing the Real Client IP
|
||
|
||
When an Xray inbound sits behind an intermediary — a CDN like Cloudflare, an L4 tunnel/relay,
|
||
or another panel — the IP that Xray sees is the **intermediary's** address, not the visitor's.
|
||
That intermediary IP is what shows up in the panel's online/IP view and what the per-client
|
||
**IP limit** counts against, which makes both useless behind a proxy.
|
||
|
||
Xray-core can recover the real visitor IP. 3x-ui exposes the two mechanisms in the inbound form
|
||
and feeds the recovered IP into the same pipeline that drives IP-limit enforcement, the online
|
||
list, and multi-node sync — so once it is set, everything downstream just works.
|
||
|
||
## Where to set it
|
||
|
||
Open an inbound → **Transport / Stream Settings** → enable **Sockopt** → use the
|
||
**Real client IP** preset selector:
|
||
|
||
| Preset | What it does | Use for |
|
||
|---|---|---|
|
||
| **Off / direct** | Clears both fields. | Inbound reachable directly by clients. |
|
||
| **Cloudflare CDN** | Sets `sockopt.trustedXForwardedFor = ["CF-Connecting-IP"]`. | WebSocket / HTTPUpgrade / XHTTP behind Cloudflare's CDN (orange cloud). |
|
||
| **L4 relay / Spectrum (PROXY)** | Sets `acceptProxyProtocol = true`. | An L4 tunnel/relay in front, or Cloudflare **Spectrum**. |
|
||
|
||
The raw `Proxy Protocol` switch and `Trusted X-Forwarded-For` list stay visible below the preset
|
||
selector for manual / advanced tuning — the presets just fill them in for you.
|
||
|
||
## Scenario 1 — Cloudflare CDN
|
||
|
||
Cloudflare's CDN (the orange cloud) forwards the visitor's IP in the `CF-Connecting-IP` request
|
||
header. Xray reads it when the transport is **WebSocket**, **HTTPUpgrade**, or **XHTTP** and
|
||
the header name is listed in `sockopt.trustedXForwardedFor`.
|
||
|
||
```json
|
||
"streamSettings": {
|
||
"network": "ws",
|
||
"sockopt": { "trustedXForwardedFor": ["CF-Connecting-IP"] }
|
||
}
|
||
```
|
||
|
||
Pick the **Cloudflare CDN** preset. You can add `X-Real-IP`, `True-Client-IP`, or `X-Client-IP`
|
||
to the list if a different upstream uses those.
|
||
|
||
> This is **not** the same as Cloudflare Spectrum. The free/CDN tier forwards HTTP headers — use
|
||
> this scenario. Spectrum (a TCP/L4 product) can send the PROXY protocol — use Scenario 2.
|
||
|
||
## Scenario 2 — L4 tunnel / relay or Cloudflare Spectrum (PROXY protocol)
|
||
|
||
For a TCP-level front (HAProxy, gost, nginx `stream`, an Xray dokodemo-door relay, or Cloudflare
|
||
Spectrum), the real IP is carried in the **PROXY protocol** header. Enable
|
||
`acceptProxyProtocol` and make sure the **upstream emits PROXY protocol** — otherwise the
|
||
connection will fail.
|
||
|
||
```json
|
||
"streamSettings": {
|
||
"network": "tcp",
|
||
"sockopt": { "acceptProxyProtocol": true }
|
||
}
|
||
```
|
||
|
||
Pick the **L4 relay / Spectrum (PROXY)** preset. Works on TCP/RAW, WebSocket, HTTPUpgrade, gRPC
|
||
and XHTTP; **not** on mKCP. The front must be configured to send the header, e.g.:
|
||
|
||
- **HAProxy**: `server backend 127.0.0.1:443 send-proxy` (or `send-proxy-v2`).
|
||
- **nginx** (`stream {}` block): `proxy_protocol on;` on the `server`, and on the upstream side
|
||
`proxy_protocol on;` in the `server` that connects to Xray.
|
||
|
||
## Transport support matrix
|
||
|
||
| Mechanism | TCP/RAW | mKCP | WebSocket | gRPC | HTTPUpgrade | XHTTP |
|
||
|---|:--:|:--:|:--:|:--:|:--:|:--:|
|
||
| `trustedXForwardedFor` (header) | – | – | ✅ | – | ✅ | ✅ |
|
||
| `acceptProxyProtocol` (PROXY) | ✅ | – | ✅ | ✅ | ✅ | ✅ |
|
||
|
||
The form shows a warning when you select a preset that the current transport cannot honor.
|
||
|
||
> **Use one, not both.** `acceptProxyProtocol` and `trustedXForwardedFor` are independent — the
|
||
> first reads the real IP from the L4 PROXY header, the second from an HTTP request header. On
|
||
> WebSocket / HTTPUpgrade / XHTTP, xray applies the HTTP header *last*, so a stale
|
||
> `trustedXForwardedFor` would override (and defeat) a PROXY-protocol setup. The presets are
|
||
> mutually exclusive and clear the other field for you; only mix them by hand if you know your
|
||
> upstream chain needs it.
|
||
|
||
## Multi-node
|
||
|
||
No extra configuration is needed. The inbound's `streamSettings` (including these sockopt
|
||
fields) is pushed to child nodes verbatim, so the node's Xray records the real IP, and the
|
||
parent panel pulls each node's per-client IPs roughly every 10 seconds. The real visitor IP
|
||
shows up on the parent automatically.
|
||
|
||
## Security note
|
||
|
||
Both `acceptProxyProtocol` and `trustedXForwardedFor` are **server-side only** — they are
|
||
stripped from subscription output, so they never reach clients. Only enable
|
||
`trustedXForwardedFor` when the inbound is genuinely behind a trusted proxy that sets the
|
||
header; otherwise a client could spoof the header and forge its own source IP.
|
||
|
||
## Verifying
|
||
|
||
1. Set the preset and save the inbound.
|
||
2. Inspect the generated Xray config and confirm `streamSettings.sockopt` carries the expected
|
||
field (`trustedXForwardedFor` or `acceptProxyProtocol`).
|
||
3. Connect through the intermediary, then open the client's IPs / online view in the panel — it
|
||
should show the real visitor IP rather than the CDN/relay address.
|