nghttp3: the HTTP/3 and QPACK layer, in C, with no QUIC of its own
HTTP/3 library written in C
At a glance
- What is it?
- nghttp3 implements RFC 9114 HTTP/3 over QUIC and RFC 9204 QPACK in C, and deliberately depends on no particular QUIC transport, so it plugs into whatever QUIC stack you already have. The library supports the dynamic table, several HTTP extensions, and optionally uses AVX2 for performance, and it is MIT licensed.
- Who is it for?
- nghttp3 fits a team writing or embedding a QUIC stack that needs the HTTP/3 mapping and the QPACK header compression done properly, and it fits one that already uses ngtcp2 and wants to stay in that family. It does not fit a project that needs server push, since the library does not support it, and it does not fit one looking for a batteries-included HTTP/3 client.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 12 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A protocol layer, deliberately separated from the transport
nghttp3 is an implementation of the RFC 9114 HTTP/3 mapping over QUIC and RFC 9204 QPACK in C, and the sentence that follows is the most important one in the README.
It does not depend on any particular QUIC transport implementation.
That single design decision is what makes the library usable in more than one place. HTTP/3 over QUIC is layered: the QUIC transport provides the streams, the connection migration and the loss recovery, and the HTTP/3 layer turns those streams into requests and responses with the right framing and header handling. A library that implements only the upper layer can sit on top of any transport, and the QUIC stack can be replaced or chosen independently.
The practical consequence is that nghttp3 is not a QUIC implementation and does not become one. If you are looking for a QUIC stack, this is the wrong repository, and the examples section points at where the pieces live. The client and server examples are in the ngtcp2 repository, and curl's own HTTP/3 support is in curl, in `lib/vquic/curl_ngtcp2.c`. The third of those is the most telling, because curl is a consumer rather than a sibling, and it is using the layering properly rather than adopting a transport it did not write.
The HTTP/3 layer is where the request and response semantics live, and getting the framing and the stream management right is the bulk of the work. The QPACK layer is where the header compression lives, and the dynamic table is the part of QPACK that requires real state management.
The library is MIT licensed, with the copyright held by the nghttp3 contributors since 2019, and the current release is v1.18.0 from 2026-07-26, following v1.17.0 in June and v1.16.0 in May 2026, with the last push on 2026-10-11.
What is implemented, and what is deliberately not
The HTTP/3 section is short and worth reading for what it leaves out.
This library implements RFC 9114 HTTP/3. It does not support server push.
Server push is the one omission, and it is a real one rather than an oversight. Server push was removed from later HTTP drafts and never saw broad deployment, so a fresh implementation skipping it is following where the protocol went rather than leaving a gap. But if you are implementing a client for an origin that does push, this library cannot do it.
The extensions list is what shows the library is tracking the current specification work rather than freezing at 9114.
The Extensible Prioritization Scheme for HTTP, RFC 9218, is implemented, which is the mechanism for expressing the relative priority of requests in a way that survives intermediaries.
Bootstrapping WebSockets with HTTP/3, RFC 9220, is implemented, so a WebSocket can be established over HTTP/3 rather than falling back to a separate handshake.
The ORIGIN Extension in HTTP/3, RFC 9412, is implemented, and the topic tags in the repository include rfc9297 as well, which is HTTP Datagrams and the Capsule Protocol, and the library can send and receive `SETTINGS_H3_DATAGRAM`.
The QPACK section says it implements RFC 9204 and that it supports the dynamic table. The dynamic table is the stateful half of QPACK, where frequently repeated header fields are held in a table and referenced by index rather than sent again. It is where the compression gains are, and it is also where the complexity lives, because the encoder and decoder have to agree on insertions and evictions or the stream desynchronises.
The topic tags name the same territory: http3, qpack, quic, rfc9114, rfc9204, rfc9218, rfc9220 and rfc9297.
The build needs a recent C11 compiler, and that is a real constraint
The requirements section is two sentences and it is stricter than it looks.
nghttp3 requires a C11 compiler to build. The modern compilers such as clang 19 or newer, gcc 15 or newer, and MSVC 2022 (1944) are known to work.
That version floor is unusual for a C library and it is worth understanding why it matters. A QUIC implementation deals with a great deal of untrusted input, from packet headers to HPACK and QPACK header blocks, and the modern toolchains bring sanitiser support, better undefined behaviour diagnostics and stronger static analysis. A library that wants to be safe by construction in that environment sets a floor rather than working around old compilers.
The practical consequence is a real constraint on adoption. A project pinned to gcc 12 or clang 14, which covers a lot of current distribution toolchains, cannot build this at the stated versions. There is no mention of support for older compilers, so the honest reading is that the floor is the floor.
The build itself follows the autotools path:
git clone https://github.com/ngtcp2/nghttp3
cd nghttp3
git submodule update --init
autoreconf -i
./configure
make -j$(nproc) checkThe `git submodule update --init` step matters, because the repository has a `.gitmodules` file. The last command runs the test suite rather than just building, and there are separate script names for it, `qifs.sh` and `qifs-check.sh`, which look like the QPACK interop format scripts used to check the implementation against the specification's test vectors.
The top-level listing shows both build systems side by side, `configure.ac`, `Makefile.am` and `m4/` for autotools, and `CMakeLists.txt`, `CMakeOptions.txt` and `cmake/` for CMake, with `cmakeconfig.h.in` for the generated configuration header. A library that ships both is a library that expects to be consumed by projects using either.
AVX2 for the hot path, and the tools that generate the tables
The optimizations section is a single paragraph and it is the kind of thing that tells you where the time goes.
This library optionally uses AVX2, if available, to optimize its performance. To compile with AVX2, add `-mavx2` to CFLAGS. Note that by default, CFLAGS is set to `-g -O2`. When specifying CFLAGS, include them as well, for example `-g -O2 -mavx2`.
The instruction to keep the default flags is not pedantry. Overriding CFLAGS replaces the default rather than adding to it, so a naive `-mavx2` loses the optimisation level and produces a slower build than the default. The library is telling you how to turn the SIMD path on without accidentally turning optimisation off.
That a header compression library has an AVX2 path at all is a signal about the workload. QPACK encoding and decoding means scanning and copying bytes in tight loops over header blocks, which is the kind of code where SIMD helps and where the difference is measurable in a proxy that handles a lot of traffic.
The other thing in the top-level listing is a set of generator scripts, and they explain the shape of the library. `genlibtokenlookup.py` generates a lookup table for the library tokens, which are the static entries every QPACK encoder and decoder shares. `mkhufftbl.py` and `mkstatichdtbl.py` generate the Huffman tables and the static header table. So a significant part of the source is generated rather than written, and the generators are in Python and live at the root of the repository.
There is a `fuzz/` directory and a `.clusterfuzzlite/` directory, so the library is fuzzed continuously through ClusterFuzzLite. For a parser of untrusted network input, continuous fuzzing is not a nice-to-have, and it is a reasonable thing for a project handling header blocks from arbitrary peers to have. There is also a `SECURITY.md` and a `CHANGELOG` and a `NEWS` file.
Where nghttp3 is the wrong choice
The first case is a project that needs server push, which the library explicitly does not support. That is narrow, but it is also a hard stop rather than a limitation to work around.
The second case is a project that wants a usable HTTP/3 client. This is a protocol library, not an application. There is no HTTP client here, no URL handling, no TLS integration, no certificate management and no user agent code. The examples in the repository are low-level, and the `examples/` directory in this project holds QPACK encoder and decoder example programs, `qpack.cc`, `qpack_encode.cc` and `qpack_decode.cc`, with shared headers including `template.h` and `util.h`. Anyone expecting a drop-in client will not find one.
The third case is a project that wants to avoid writing a QUIC stack. The layering is a virtue precisely because it means you have to supply the transport, so if your goal is a complete HTTP/3 implementation in one dependency, this is not it. The ngtcp2 project is the sibling that provides the transport, and the examples point there for a working client and server.
The fourth is a toolchain constraint. The stated requirement of a C11 compiler with clang 19, gcc 15 or MSVC 2022 known to work excludes a number of current environments, including many distribution toolchains and some embedded cross-compilation setups. That is a deliberate choice and it is probably right for a security-sensitive parser, but it does narrow who can build it.
A final note on the QPACK state. The dynamic table is supported, and that is the correct choice, but it means the encoder and the decoder hold state per connection and per stream. An implementation that has to interleave many streams will need to manage the table capacity and the blocked streams carefully, and the library's own QPACK example programs and the interop test scripts are the place to see how that is expected to be used.
Editorial conclusion
nghttp3 fits a team writing or embedding a QUIC stack that needs the HTTP/3 mapping and the QPACK header compression done properly, and it fits one that already uses ngtcp2 and wants to stay in that family. It does not fit a project that needs server push, since the library does not support it, and it does not fit one looking for a batteries-included HTTP/3 client. Before you adopt it, read the Requirements section, because the library needs a C11 compiler and the known-good set is specific, clang 19 or newer, gcc 15 or newer, or MSVC 2022, which excludes many current toolchains.
Frequently asked questions
What does nghttp3 implement?
It implements RFC 9114 HTTP/3 over QUIC and RFC 9204 QPACK in C, with support for the dynamic table. It also implements the Extensible Prioritization Scheme for HTTP, RFC 9218, Bootstrapping WebSockets with HTTP/3, RFC 9220, and the ORIGIN Extension, RFC 9412, and can send and receive SETTINGS_H3_DATAGRAM.
Does nghttp3 need a specific QUIC implementation?
No. It does not depend on any particular QUIC transport implementation, which lets it sit on top of whatever QUIC stack a project already has. The client and server examples live in the ngtcp2 repository, and curl uses it in `lib/vquic/curl_ngtcp2.c`.
How do I build nghttp3?
Clone the repository, run `git submodule update --init`, then `autoreconf -i`, `./configure` and `make -j$(nproc) check`. A C11 compiler is required, with clang 19 or newer, gcc 15 or newer, or MSVC 2022 known to work.
Can I enable AVX2 in nghttp3?
Yes, by adding `-mavx2` to CFLAGS, but keep the default flags as well, so `-g -O2 -mavx2` rather than `-mavx2` alone, since specifying CFLAGS replaces the default of `-g -O2` rather than adding to it.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ngtcp2-nghttp3)Was this useful?