Your relay's version number is lying to you about an HTTP/3 bug
A header-compression flaw lets one connection exhaust an HTTP/3 server's memory. Hysteria2, Xray and sing-box all inherit it through quic-go, and many builds that look patched are not.
If you updated Hysteria2 at some point this spring, ran go version -m on the binary and saw quic-go v0.59.1-0.20260425…, you would be forgiven for assuming you had the May security fix, but you don't. That version string describes code that sits before 0.59.1, not after it, and I've confirmed byte for byte that the function the fix touches is unchanged.
That matters this week because the bug class behind it has just gone public. On 20 October FoxIO published "Header Accordions", a study of how HTTP/3 servers decompress request headers. Seven of the 10 implementations it tested could be made to allocate vastly more memory than the attacker sends, enough for a single connection to knock a default-configured server over. quic-go, the Go library underneath a large share of the modern proxy world, was one of them.
How a few kilobytes fill a server
HTTP/3 compresses headers with a scheme called QPACK. Rather than resend the same cookie or user agent on every request, a client can store a header once in a shared "dynamic table" and then refer to it with an index, often a single byte. The server expands those references back into full headers, because that is what the application reads.
The memory cost is therefore set by the decoded size, not by what crossed the wire. The HTTP/3 specification expects servers to cap that decoded size and to charge 32 bytes of overhead per field, so that thousands of tiny headers can't slip under the limit. FoxIO found implementations that measured the compressed size instead, skipped the overhead, shipped with no limit, or never enforced one.
It describes two attacks. One packs roughly 160,000 unique, tiny header names into a 1 MiB frame. The other stores a single 4 KiB value in the dynamic table and references it thousands of times with one-byte indexes, which on the worst implementations works out at about 4,064 bytes allocated for every byte sent. quic-go was less dramatic: FoxIO measured around 17 times the encoded size retained on the heap, and quic-go's own advisory puts the worst case at about 50. Neither figure is comforting on a 1 GB VPS.

Two bugs, two fixes
quic-go has fixed this twice, in two places.
The headers bug, CVE-2025-64702, affects versions up to 0.56.0 and was fixed in 0.57.0 in late 2025, which is why it carries last year's number. The fix advertises a maximum decoded header size, checks the running total as each field is decoded, and refuses oversized requests with an HTTP 431.
The trailers bug, CVE-2026-40898, is the same mistake in the code that handles trailers, the headers that can follow a request body. It affects versions up to 0.59.0 and was fixed in 0.59.1, tagged in May and disclosed on 31 May.
Both are rated Moderate, CVSS 5.3, and both are denial of service only. Nobody is running code on your server or reading your traffic through this. They can, however, flatten your relay without credentials and keep doing it for as long as they find it entertaining. Both advisories also note that clients are affected, since quic-go decodes headers the same way in each direction, so a hostile server could crash a client in turn.
Who is in range
The flaw lives in quic-go's http3 package, so the question is whether your software speaks HTTP/3 through it.
Hysteria2 is squarely exposed. Its protocol specification requires every client to authenticate with an HTTP/3 request immediately after the QUIC handshake, which means the vulnerable parser runs before the server knows who you are. There is no setting that avoids it: HTTP/3 is the front door.
Xray is exposed if you use the XHTTP transport over HTTP/3, which pulls in the same quic-go fork as Hysteria2.
sing-box carries its own quic-go fork for its HTTP/3 and QUIC-based inbounds, Hysteria2 included.
Caddy is exposed whenever HTTP/3 is on, and it is on by default for HTTPS sites. That covers anything you have put behind Caddy, from a panel to a subscription endpoint.
VLESS with REALITY over TCP, WireGuard, AmneziaWG and OpenVPN don't touch HTTP/3 and aren't affected.
I have not traced every QUIC-based protocol through each codebase, so treat the line between "uses QUIC" and "reaches the HTTP/3 parser" as the one place in this piece where some caution is warranted.
The pseudo-version trap
None of the three proxy cores uses upstream quic-go directly. Hysteria2 and Xray pin a fork maintained by apernet, and sing-box pins one maintained by SagerNet. Go records a fork commit as a pseudo-version, and the format is easy to misread.
v0.59.1-0.20260425001925-6c6cc9bcb716 does not mean "0.59.1 plus some extras". In Go's versioning rules it means "a commit after 0.59.0, heading towards 0.59.1", and it sorts below the real 0.59.1. Whether such a commit contains the fix depends entirely on what the fork maintainer merged.

So I checked: I pulled each project's go.mod at its release tags and compared the trailer-parsing function in each fork against upstream. The apernet commit of 25 April used by Hysteria2 2.9.1 and 2.9.2 and by Xray v26.7.28, and SagerNet's v0.59.0-sing-box-mod.4 used by every sing-box 1.13 release, are byte-identical to the unfixed 0.59.0. The earlier Hysteria2 and Xray releases in that range pin fork commits from March and April that predate it, so they are no better off. The fork commits used from Hysteria2 2.9.3, Xray v26.9.8 and sing-box 1.14.0 onwards are byte-identical to the fixed 0.59.1.
Here is where that leaves each project:
- Hysteria2: 2.6.5 and older lack both fixes. 2.7.0 and 2.7.1 have the headers fix only. 2.8.0 to 2.9.2 carry the misleading pseudo-version and still lack the trailers fix. 2.9.3 and later have both.
- Xray: v25.10.15 and older lack both. v25.12.x to v26.7.28 have the headers fix only, misleading pseudo-versions included. v26.9.8 and later have both.
- sing-box: 1.12.x lacks both; I found no sign of a backport. Every 1.13.x release has the headers fix only. 1.14.0 and later have both.
- Caddy: 2.10.2 and older lack both. 2.11.0 to 2.11.2 have the headers fix only. 2.11.3 and later have both.
The awkward part is how many people sit in those middle rows. Panels and one-click install scripts tend to pin a core version and stay there, and sing-box 1.13 ran to 22 point releases, every one of them missing the trailers fix. Phone clients such as v2rayNG, NekoBox and Hiddify embed these cores too, so the app version matters as much as the server's.
Checking what you actually run
Each tool will report its own version with hysteria version, xray version, sing-box version or caddy version. Compare against the list above.
For any Go binary, including ones you didn't build, ask Go what was compiled in:
go version -m /usr/local/bin/hysteria | grep quic-go
You are looking for the quic-go line, upstream or fork. Anything below 0.59.1, including a pseudo-version starting v0.59.1-0., lacks the trailers fix unless it is one of the fork commits named above as fixed. Upstream quic-go is now on the 0.6x series, and the safe Hysteria2, Xray and sing-box releases all use forks based on 0.60 or later.
If you can't update Caddy straight away, you can turn HTTP/3 off until you can, by adding protocols h1 h2 inside a servers block in the global options. Hysteria2 offers no such escape hatch, which is the polite way of saying the only mitigation is the update.
The verdict
This is a moderate bug with an outsized effect on the people who read this site. A relay that speaks HTTP/3 can be taken offline by anyone who can reach its port, censors included, and the version strings in the forks have left a good number of administrators who updated this year believing they were covered. That is the main finding here, and nobody else seems to have mapped it to the tools people actually run.
What to do
- Upgrade to Hysteria2 2.9.3, Xray v26.9.8, sing-box 1.14.0 and Caddy 2.11.3 or later. Current releases are well past those lines, so simply taking the latest is fine.
- Check the binary that is actually running with
go version -m, not the tag you think you installed. Panels and install scripts are the usual culprits for a stale core. - Update the client apps on your phones and those of anyone who uses your server.
- If a Caddy instance can't be upgraded today, disable HTTP/3 on it.
- If you run VLESS with REALITY over TCP and nothing else, you can leave this one for the weekend. If the same box also runs Caddy, Hysteria2 or XHTTP over h3, it shouldn't wait that long.
Elsewhere this week, the eMudhra certificate compromise is the other item proxy operators should look at, because old Go builds are part of that story too.