Summary
Xray v25.10.15 (PR #5101) introduces a VLESS simplified reverse proxy feature where a specific user entry in a VLESS inbound includes a reverse field:
{
"tag": "r-inbound",
"port": 8443,
"protocol": "vless",
"settings": {
"users": [
{
"id": "ac04551d-6ebf-4685-86e2-17c12491f7f4",
"flow": "xtls-rprx-vision",
"reverse": {
"tag": "r-outbound"
}
}
],
"decryption": "mlkem768x25519plus.native.600s.aCF82eKiK6g0DIbv0_nsjbHC4RyKCc9NRjl-X9lyi0k"
}
}
This allows a device behind NAT/firewall (no inbound ports open) to establish a reverse tunnel to a public server without any overlay network such as Tailscale.
References:
Problem
Remnawave’s config processing pipeline makes this impossible for three reasons:
Issue 1: cleanInboundClients() unconditionally wipes all VLESS users/clients
In src/common/helpers/xray-config/xray-config.validator.ts:
public cleanInboundClients(injectFlow: boolean): void {
for (const inbound of this.config.inbounds) {
if (!this.hasManagedClients(inbound)) continue;
this.ensureSettings(inbound);
inbound.settings!.clients = []; // always wiped
}
}
Any reverse-annotated user entry written in the config profile is destroyed before xray receives the config.
Issue 2: addUsersToInbound() only injects { id, email } — no reverse field
case 'vless':
inbound.settings.clients ??= [];
for (const user of users) {
inbound.settings.clients.push({
id: user.vlessUuid,
email: user.tId.toString(),
// 'reverse' field is never injected
});
}
There is no mechanism in the user data model or squad system to carry a reverse field through to the injected config.
Issue 3: leaveInbounds() excludes unregistered VLESS inbounds
public leaveInbounds(tags: Set): void {
this.config.inbounds = this.config.inbounds!.filter(
(inbound) => tags.has(inbound.tag!) || !this.hasManagedClients(inbound),
);
}
Since vless is a managed protocol, a reverse-only inbound not present in activeInbounds is stripped from the config. But if it is added to activeInbounds, its reverse-annotated users get wiped by Issue 1.
Use Case
A device sits behind a home router where the firewall cannot be modified (no open inbound ports). The VLESS simplified reverse proxy in Xray v26 is exactly designed for this NAT traversal scenario — the internal device initiates an outbound connection to the public server and establishes a persistent reverse tunnel. This feature cannot be used with Remnawave as the panel.
Expected Behavior
One of the following would resolve this:
-
Preferred: Add a “reverse proxy user” concept to the squad/inbound system, where a special non-subscribing user entry carries a
reverse: { tag: "..." }field that is injected as-is into the target VLESS inbound’susersarray, bypassingcleanInboundClientsfor that entry. -
Alternative: Introduce a flag on an inbound registration (e.g.
isReverseProxy: true) that causes Remnawave to skipcleanInboundClientsfor that inbound and pass the rawusersarray from the config profile directly to xray unchanged.
Environment
- Remnawave backend: v2.7.4
- Xray-core: v26.3.27
- xray-typed: v1.1.1