Open-source project
h2o/h2o avatar
h2o/h2o

H2O (h2o/h2o): the C HTTP server that speaks HTTP/1, HTTP/2 and experimental HTTP/3

H2O - the optimized HTTP/1, HTTP/2, HTTP/3 server

11,546 stars883 forksCMIT

At a glance

What is it?
H2O is an MIT-licensed HTTP server written in C, usable as a standalone server or as a library, with HTTP/3 support the README labels experimental. The repository still receives commits, but version tags stopped in 2019.
Who is it for?
Adopt h2o/h2o if you want an embeddable HTTP stack in C, or a server whose HTTP/3 support is explicitly labelled experimental and whose configuration is plain YAML. Do not adopt it if you need a tagged release line: the newest tag is v2.3.0-beta2 from 2019, and the repository now carries a tag that says versions are no longer tagged.
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 1 day 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 September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What h2o/h2o solves, and who it is for

Most HTTP servers are distributed as binaries you configure and run. H2O is distributed as a C codebase that produces both a server and a library. The README states it can be used as a library, and the repository layout backs that up: libh2o.pc.in and libh2o-evloop.pc.in are pkg-config templates, and examples/libh2o/ exists as a separate example set from examples/h2o/. That split is the point. If you are writing a C or C++ service and you need an HTTP/1.x and HTTP/2 stack inside your own process rather than behind a reverse proxy, the library path is the reason to look here. If you just want to serve static files and terminate TLS, the server binary is the path.

The second audience is people who care about protocol coverage in one process. The README describes H2O as an optimized HTTP server with support for HTTP/1.x, HTTP/2 and HTTP/3, and marks HTTP/3 as experimental. One codebase covering all three means you do not run a separate terminator for QUIC traffic. The cost is that the newest protocol is the least settled part of the tree, which the README says outright rather than burying.

How the server and the library are structured

The top level separates include/, lib/, src/, deps/ and share/. include/ holds public headers, lib/ holds the library implementation, src/ holds the server, and deps/ holds bundled dependencies pulled in through .gitmodules. That is a conventional C project shape, and it tells you the library boundary is real rather than a build artifact: the headers you would link against live in include/, not in src/.

Two pkg-config files ship in the root. libh2o.pc.in and libh2o-evloop.pc.in correspond to two integration styles. The evloop variant points at an event-loop-based embedding, which is the model you want when your application already owns a loop. The plain libh2o variant is the more direct one. The README does not compare them, so the decision has to come from the headers and the examples/libh2o/ directory.

Configuration is not compiled in. The examples/ directory contains h2o/ for server configuration and h2o_mruby/ for the mruby handler path, and documentation lives under doc/ and srcdoc/. The practical consequence is that a deployment is a YAML file plus a binary, not a rebuild. That is a meaningful operational difference from servers configured at compile time, and it is also why the configuration reference in doc/ matters more than the README for day-to-day work.

Building H2O from source and serving a first directory

The README does not carry install instructions. It points to the documentation at h2o.examp1e.net for more information, and the repository carries CMakeLists.txt at the top level, so CMake is the build entry point the tree exposes. The repository also ships a .dockerignore, which implies container builds are anticipated, but no Dockerfile is listed among the top-level entries, so do not assume one exists.

Because the README gives no build commands, there is nothing to quote here verbatim, and inventing a CMake invocation would be worse than leaving it out. What the tree does tell you is the shape of the result: CMakeLists.txt drives the build, and the artifacts are the server plus the two library pkg-config files. The example configuration format is the part that can be described from the repository files rather than guessed at.

The examples/ directory holds the configuration samples. examples/h2o/ is the server configuration set, examples/h2o_mruby/ is the mruby handler set, and examples/doc_root/, examples/doc_root.alternate/ and examples/doc_root.third/ are ready-made document roots you can serve before pointing the server at your own files. The configuration keys themselves are documented under doc/ and srcdoc/, not in the README, so read those before writing a config file rather than copying keys from a blog post.

Once you have a configuration file, the server takes it as an argument, and you request a path from the document root you configured to confirm the file contents come back. The README does not document the exact invocation, so check doc/ for the current form.

The release-tag problem is the real adoption risk

The most recent release entry is not a version. It is a tag whose own message reads "we no longer tag versions", dated 2026-01-19. Before that, the newest tags are v2.3.0-beta2 and v2.2.6, both from 2019-08-13. The last push to the repository was 2026-09-10, so code is moving, but there is no version number attached to what is moving.

That combination is workable and unpleasant at the same time. Workable, because a commit hash is a perfectly good pin and the project clearly intends you to use one. Unpleasant, because every downstream consumer that expects a version string, a changelog entry per release, or a distro packaging workflow keyed to tags has to build its own convention. The Changes file exists at the top level, so there is a record of what changed, but it is not sliced into releases anymore.

If your organisation requires a vendor-supported release cadence with a version number and a maintenance window, this project does not offer one, and no amount of reading the documentation will change that. Pin a commit and track Changes yourself, or pick a server with a tagged release line.

Where H2O is the wrong choice

HTTP/3 is labelled experimental in the README. Treat that label as load-bearing. If your requirement is a QUIC terminator you can put in front of paying traffic today, the README itself is telling you this is not that, and nothing in the repository layout contradicts it.

The second wrong-tool case is the opposite of the first audience. If you do not have a reason to link a C library, the library advantage is worth nothing to you, and you are choosing H2O purely as a server. At that point you are comparing configuration formats and operational ergonomics, and H2O's YAML configuration is one option among several rather than a differentiator. The mruby handler in examples/h2o_mruby/ is a genuine extension point, but it is also a language runtime you now have to build and reason about.

Third, the build brings in deps/ through submodules. A source build therefore depends on submodule checkout working in your environment. If your build system does not handle submodules, that is a failure mode you will hit before you ever reach the configuration file.

How it differs from nginx and Apache httpd

The honest comparison is about packaging and extension model, not about which one is faster. nginx and Apache httpd both ship versioned releases and both have module ecosystems that let you add behaviour without touching the core. H2O's extension story runs through its configuration and through the mruby handler in examples/h2o_mruby/, and its release story runs through commit hashes rather than tags.

The library angle is the sharpest difference. Neither nginx nor Apache httpd is designed to be linked into your process as an HTTP stack; H2O is, and the pkg-config files and include/ directory are the evidence. If you are embedding, that is a real architectural difference rather than a preference. If you are not embedding, the difference mostly disappears and you are left comparing YAML against nginx.conf against httpd.conf.

Protocol coverage cuts the other way in one respect. H2O carries HTTP/1.x, HTTP/2 and HTTP/3 in one tree, with HTTP/3 marked experimental. That is a single-process story for all three, which is convenient, but it also means the least mature protocol shares a codebase and a release cadence with the most mature ones, and there is no version number to tell you which state you built.

Licence, maintenance and what a commit pin costs you

H2O is licensed under the MIT License, per the README and the LICENSE file at the top level. MIT is permissive: it allows use, modification and redistribution with the licence text retained. The README credits a long list of contributors and DeNA Co., Ltd. among the copyright holders, so if you redistribute, keep the notice intact. That is a description of the licence text, not legal advice; your own counsel should confirm how it interacts with your distribution model.

The maintenance picture is mixed and worth stating plainly. The repository is not archived, and the last push was on 2026-09-10, so work is landing. But the newest version tag is v2.3.0-beta2 from 2019-08-13, and the project has since published a tag stating that versions are no longer tagged. There is no supported release line to upgrade along.

Upgrade cost follows from that. You cannot diff v2.2.6 against a successor, because there is no successor tag. You read Changes, you pin a commit, and you re-read the configuration documentation under doc/ when you move the pin. Budget for that as recurring work rather than a one-time install. Teams that cannot absorb an untagged dependency should not adopt this one, regardless of how well the protocol coverage fits.

Editorial conclusion

Adopt h2o/h2o if you want an embeddable HTTP stack in C, or a server whose HTTP/3 support is explicitly labelled experimental and whose configuration is plain YAML. Do not adopt it if you need a tagged release line: the newest tag is v2.3.0-beta2 from 2019, and the repository now carries a tag that says versions are no longer tagged. Before deploying, read doc/configure.md in the tree, check Changes for the behaviour that moved since v2.2.6, and confirm the HTTP/3 status in the README rather than assuming it is production-ready.

Frequently asked questions

What is h2o/h2o?

It is an HTTP server written in C with support for HTTP/1.x, HTTP/2 and HTTP/3, where the README marks HTTP/3 as experimental. It is licensed under the MIT License and can also be used as a library.

Is h2o/h2o actively maintained?

The repository is not archived and the last push was on 2026-09-10, so commits are landing. However, the newest version tag is v2.3.0-beta2 from 2019-08-13, and a later tag states that versions are no longer tagged.

What licence does h2o/h2o use?

The README and the LICENSE file at the top level state the MIT License. The README credits DeNA Co., Ltd. and a list of individual contributors as copyright holders.

Can h2o/h2o be used as a library rather than a server?

Yes. The README states it can be used as a library, and the repository ships libh2o.pc.in and libh2o-evloop.pc.in pkg-config templates plus an examples/libh2o/ directory.

Official sources

  1. h2o/h2o on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/h2o-h2o.svg)](https://hysenlabs.com/projects/h2o-h2o)