Summary
The Hysteria2 inbound never writes UDP entries to the access log. TCP entries are written normally. VLESS inbounds in the same instance log both TCP and UDP.
This makes UDP traffic through a Hysteria2 inbound effectively invisible: there is no way to tell which user sent UDP, where it went, or whether it was dispatched at all.
Version
Reproduced on both:
Xray 26.3.27 (d2758a0)
Xray 26.7.28 (5ca6f4b)
Config
{
"log": { "access": "/cfg/access.log", "loglevel": "warning" },
"stats": {},
"policy": {
"levels": { "0": { "statsUserUplink": true, "statsUserDownlink": true } },
"system": { "statsInboundUplink": true, "statsInboundDownlink": true }
},
"inbounds": [{
"tag": "HY-TEST",
"listen": "127.0.0.1",
"port": 2099,
"protocol": "hysteria",
"settings": { "version": 2, "clients": [{ "auth": "secret123", "email": "tester42" }] },
"sniffing": { "enabled": true, "destOverride": ["http", "tls", "quic"] },
"streamSettings": {
"network": "hysteria",
"security": "tls",
"tlsSettings": {
"alpn": ["h3"],
"certificates": [{ "certificateFile": "/cfg/c.pem", "keyFile": "/cfg/k.pem" }]
},
"hysteriaSettings": { "version": 2 }
}
}],
"outbounds": [{ "tag": "direct", "protocol": "freedom" }]
}
Steps to reproduce
Official hysteria client with both a SOCKS5 listener and a UDP forwarder:
server: 127.0.0.1:2099
auth: secret123
tls:
sni: test.local
insecure: true
socks5:
listen: 127.0.0.1:10999
udpForwarding:
- listen: 127.0.0.1:15999
remote: 8.8.8.8:53
timeout: 10s
Then generate one TCP flow and two UDP flows:
curl --socks5-hostname 127.0.0.1:10999 https://api.ipify.org
dig @127.0.0.1 -p 15999 example.com +short
dig @127.0.0.1 -p 15999 github.com +short
Both dig calls return correct answers, so the UDP relay itself works.
Actual access log
2026/08/01 16:50:11.340278 from 127.0.0.1:46365 accepted tcp:api.hy2.io:443 [HY-TEST >> direct] email: tester42
2026/08/01 16:50:16.311408 from 127.0.0.1:46365 accepted tcp:api.ipify.org:443 [HY-TEST >> direct] email: tester42
Two TCP lines. Zero UDP lines, despite two successful UDP round trips.
Expected
A line per UDP session, the way a VLESS inbound produces them:
... accepted udp:8.8.8.8:53 [VLESS-IN -> direct] email: someone
Additional detail
At "loglevel": "info" the UDP flow does appear in the error log, but without the inbound tag and without the user:
[Info] [4011292065] app/dispatcher: default route for udp:8.8.8.8:53
[Info] [4011292065] proxy/freedom: connection opened to udp:8.8.8.8:53, local endpoint [::]:38973, remote endpoint 8.8.8.8:53
For TCP there is a corresponding proxy/hysteria: tunnelling request to tcp:... line; for UDP there is no proxy/hysteria line at all, which suggests the UDP path bypasses the code that emits the access message.
Because no line in the info log ties that connection id to a user or an inbound, the UDP flow cannot be attributed even with verbose logging on.
Impact
On a multi-user server this makes latency and connectivity complaints on UDP-heavy workloads (games, VoIP) undiagnosable per user, while the same workloads are fully traceable on VLESS inbounds.
Scale of the gap on a live server
Same instance, same access log, over ~54k Hysteria2 lines and 200k VLESS lines:
| Inbound |
lines |
with udp: |
| Hysteria2 |
54453 |
0 |
| VLESS |
200000 |
75650 |
Summary
The Hysteria2 inbound never writes UDP entries to the access log. TCP entries are written normally. VLESS inbounds in the same instance log both TCP and UDP.
This makes UDP traffic through a Hysteria2 inbound effectively invisible: there is no way to tell which user sent UDP, where it went, or whether it was dispatched at all.
Version
Reproduced on both:
Xray 26.3.27 (d2758a0)Xray 26.7.28 (5ca6f4b)Config
{ "log": { "access": "/cfg/access.log", "loglevel": "warning" }, "stats": {}, "policy": { "levels": { "0": { "statsUserUplink": true, "statsUserDownlink": true } }, "system": { "statsInboundUplink": true, "statsInboundDownlink": true } }, "inbounds": [{ "tag": "HY-TEST", "listen": "127.0.0.1", "port": 2099, "protocol": "hysteria", "settings": { "version": 2, "clients": [{ "auth": "secret123", "email": "tester42" }] }, "sniffing": { "enabled": true, "destOverride": ["http", "tls", "quic"] }, "streamSettings": { "network": "hysteria", "security": "tls", "tlsSettings": { "alpn": ["h3"], "certificates": [{ "certificateFile": "/cfg/c.pem", "keyFile": "/cfg/k.pem" }] }, "hysteriaSettings": { "version": 2 } } }], "outbounds": [{ "tag": "direct", "protocol": "freedom" }] }Steps to reproduce
Official
hysteriaclient with both a SOCKS5 listener and a UDP forwarder:Then generate one TCP flow and two UDP flows:
Both
digcalls return correct answers, so the UDP relay itself works.Actual access log
Two TCP lines. Zero UDP lines, despite two successful UDP round trips.
Expected
A line per UDP session, the way a VLESS inbound produces them:
Additional detail
At
"loglevel": "info"the UDP flow does appear in the error log, but without the inbound tag and without the user:For TCP there is a corresponding
proxy/hysteria: tunnelling request to tcp:...line; for UDP there is noproxy/hysterialine at all, which suggests the UDP path bypasses the code that emits the access message.Because no line in the info log ties that connection id to a user or an inbound, the UDP flow cannot be attributed even with verbose logging on.
Impact
On a multi-user server this makes latency and connectivity complaints on UDP-heavy workloads (games, VoIP) undiagnosable per user, while the same workloads are fully traceable on VLESS inbounds.
Scale of the gap on a live server
Same instance, same access log, over ~54k Hysteria2 lines and 200k VLESS lines:
udp: