Releases: uNetworking/uWebSockets
v20.76.0 (A decade of uWS)
Ten years ago I made the initial commit for uWebSockets. For this release I've attached a retrospective you might find interesting. Either way, it serves as documentation.
How did we end up here?
2014–2015 | The Performance Gap
As a GPGPU programmer, benchmarking the new and widely hyped Node.js reveals a massive disparity between marketing and reality, especially considering its flagship libraries. With Express and Socket.IO running orders of magnitude slower than the equivalent C libraries, the ecosystem is found to be architecturally flawed and over-hyped. Still, the hype of Node.js and its ability to create native C++ addons invites an interest in building something better.
2016 | The Prototype (node-lws)
The first prototype, node-lws, is built to wrap libwebsockets because that was the best overall performing C library of the time. However, further benchmarking reveals a shocking truth: the library’s parser is processing data byte-by-byte in a switch statement—the least efficient implementation possible. Realizing that even the "low-level" industry standards are 10x slower than the hardware permits, it becomes clear that we need a solution from scratch.
2016–2018 | The "Drop-in" Strategy (v0.1–v0.14)
To maximize adoption, uWS is designed as a seamless, drop-in replacement for the popular ws module. Since the ws interface builds on the EventEmitter of the Node.js HTTP server, so must uWS. In order to achieve a seamless bridge between net.Socket and uWS sockets, hacky "black magic" is necessary. This necessitates a "house of cards" architecture: using Unix hacks, opaque memory interpretations, and pure prayer in the OpenSSL state. The module is a viral hit, gaining hundreds of millions of downloads.
2018 | The Architectural Pivot
Seamless compatibility becomes a double-edged sword. While it unlocks immediate traction, it ironically allows uWS to be plugged directly into the modules that were originally found to be the very bloat that warranted uWS itself, effectively neutralizing the performance endeavor. To fix this, and simultaneously remove the hacks and dependency on the Node.js HTTP server, uWS introduces its own HTTP server, URL router, pub/sub feature and breaks the compatibility by introducing its own App-based interface.
2020 | Weathering the Storm
The break in compatibility triggers a storm of online dissatisfaction. Speculative rumors that uWS is deprecated spread across Reddit and various forks pop up. However, as the industry gradually adopts the new architecture, it becomes widely accepted as the superior variant and the forks all die off. The new standalone uWS architecture is modular and testable, offers more high-level features, and receives the highest OSS-Fuzz reward from Google for its "Ideal fuzzing integration." It also brings the fastest HTTP server support available for Node.js.
2022 | The Bun Foundation
Bun adopts uSockets and uWebSockets as its networking, eventing, and crypto foundation (since v0.0.76). The performance standards pioneered by uWS for Node.js become the literal baseline for the next generation of JavaScript runtimes, mainstreaming the architectural solutions uWS was the first to identify. While we have no relation with the Bun team and don't claim any of its success, Bun is clearly a spiritual successor to what uWS started. Although with a wider goal.
2025–Present | The DeFi Transition
Having powered some of the world’s largest centralized exchanges for nearly a decade, uWS remains a preferred choice as the industry moves toward Decentralized Finance (DeFi). Today, the architecture continues to be selected as the underlying server for several of the world’s largest DEXs, proving that its core principles are as relevant for modern on-chain trading as they were for the traditional web.
Security retrospect
CVEs
We've had 2 CVEs since inception.
| Year | CVE ID | Summary | Context |
|---|---|---|---|
| 2016 | CVE-2016-10544 | Max Payload DOS | Affected early versions with compression. Triggered by JS string limits. |
| 2020 | CVE-2020-36406 | Pub/Sub Logic | Caught by our own fuzzing. Only applicable to specific MQTT wildcard usage. |
Project Integrity & Maintenance
uWS maintains a decade-long record of zero supply chain incidents, built on a foundation of strict maintainer-only control with no third-party write access. All critical services are hardened via FIDO2 hardware keys, and every release is strictly traceable to its specific Git commit. Development occurs exclusively on encrypted workstations, ensuring that the code remains as secure as the high-stakes financial systems it powers.
What's changed in this release
- Added
UWS_REMOTE_ADDRESS_USERSPACE: Use this compiler macro to cache remote addresses in userspace. It avoids kernel overhead forgetRemoteAddress()calls, nearly doubling performance for IP-heavy apps at the cost of 16 bytes per socket. - New
onDataV2Setter: We’ve introduced a superior data handler that replaces thebool isLastflag withuint64_t maxRemainingBodyLength.- Equivalence:
isLastis now(maxRemainingBodyLength == 0). - Pre-allocation: This provides a unified heuristic for both Chunked (returns
UINT64_MAXor0) and Content-Length (returns exact bytes) streaming. IfmaxRemainingBodyLengthis non-zero and below your memory threshold, you can safely pre-allocate.
- Equivalence:
- Compatibility: The original
onDataremains available for now but is implemented as a wrapper aroundonDataV2.
// Example of the new V2 logic:
res->onDataV2([](std::string_view chunk, uint64_t maxRemainingBodyLength) {
if (maxRemainingBodyLength == 0) {
/* Done! */
} else if (maxRemainingBodyLength < 1024 * 64) {
/* Optimal: we know exactly how much to reserve() */
}
});Closing thoughts
As the timeline alludes, uWebSockets now provides an interface with seven years of near-perfect backward compatibility. An app written for v15.0.0 remains functionally identical on v20.x.x. This stability is intentional. Our top priority is to provide a rock-solid foundation for the most demanding services in the world, ensuring that performance never comes at the cost of reliability.
Commercial Support & Consulting: Inquiries are welcome via email.
v20.75.0
- Adds UWS_USE_SIMDUTF for using simdutf instead of default Utf8 validation
- Show method and URL in terminating error message
- Fixes some compilation error for modern C++23 compilers
- Adds secure reloading gzip file server example
v20.74.0
Minor libdeflate correction
- libdeflate is now compatible with uWS::DEDICATED_DECOMPRESSOR and will be used if (and only if) the WebSocket's negotiation downgraded to equivalent of uWS::SHARED_DECOMPRESSOR (client_no_context_takeover). This will boost performance when possible. Of course, uWS::SHARED_DECOMPRESSOR as decompressor will always use libdeflate.
- In other cases, zlib will be used regardless.
v20.73.0
libdeflate as inflation ("decompression") fast path
Resurrecting support of libdeflate, now specifically only for inflation and only as a fast path to zlib.
WITH_LIBDEFLATE=1 make
builds examples using this fast path (you need to build the submodule first). Improved load_test.c benchmark can now highlight inflation cost:
For me and my particular test system:
- With dynamically linked zlib - 110k msg/sec inflated and echoed 300 byte JSON messages
- With statically linked zlib-cloudflare - 130k
- With libdeflate as fast path - 180k
- Without any inflation - 320k
Note: WITH_DEFLATE is only compatible with uWS::SHARED_DECOMPRESSOR!
v20.72.0
Prepared Messages for Efficient Sending and Publishing
A feature from v0.14, previously removed due to lack of interest, has now been revived! Check out the demo in examples/Precompress.cpp to see it in action.
A PreparedMessage is a message that is compressed once but can be sent multiple times. This requires the SHARED_COMPRESSOR option but works with any decompressor option. The core idea is to decouple the compression process from the act of sending the message, reducing the per-socket computational overhead of compression to near zero.
Ideal Use Case
This feature is ideal for scenarios where you need to send snapshots of a data stream to multiple recipients. Here’s how it could work:
- A timer periodically creates a new
PreparedMessagecontaining the latest snapshot of the data (e.g., every 500ms). - When a WebSocket connects, it receives the most recent prepared snapshot, followed by incremental updates (deltas) from that point onward.
- If the snapshot is large, pre-compressing it as a
PreparedMessageeliminates the need for repeated compression, significantly reducing overhead for each socket.
Important Notes
- This feature only works with
SHARED_COMPRESSOR. Using a sliding window compression method is incompatible with this optimization. - New methods have been added to support this feature:
WebSocket::sendPreparedApp::publishPreparedLoop::prepareMessage
- You can create
PreparedMessageobjects on any thread, as long as that thread has its ownLoop(which is automatically created for you viaLoop::get()). However, in such cases, you must handle synchronization manually. Refer toexamples/Precompress.cppfor guidance.
This release introduces the interface and its initial implementation. While memory-saving optimizations are not included in this release, they may be addressed in future updates.
v20.71.0
- Examples are built in parallel
- Supports building with all _LIBCPP_HARDENING_MODE
- (Experimental) cached HTTP support is reverted for now, restoring fuzz builds
- h1spec and test subject is added to CI
- Disallow duplicate Host headers as per RFC9112
v20.70.0
v20.69.0
HTTP spec. compliance testing
A basic HTTP RFC9112 spec. compliance test has been added and minor changes have been made to pass all tests immediately:
This basic test catches no issues in Node.js, but it catches 2 issues in Deno (reported). The test can be found here:
https://github.com/uNetworkingAB/h1spec
In contrast, running the test on completely broken servers like mrhttp you get an obvious indication of so:
v20.68.0
Improved (restored) backwards compatibility
For some users, recent introduction of CachedApp broke seamless backwards compatibility. If you rely on uWS::App being uWS::TemplatedApp<false> and not something else (like uWS::CachedApp<false> 😉), this release fixes your build:
typedef uWS::TemplatedApp<false> App;
typedef uWS::TemplatedApp<true> SSLApp;
v20.67.0
- Adds removeChildApp



