# cinatra: a header-only C++20 HTTP server built on coroutines

> A header-only HTTP framework for C++20 with a coroutine server, a coroutine client, websocket and TLS support, and compile-time SIMD switches. Written for people who want an embedded server without a build system or a dependency tree.

**qicosmos/cinatra** — A cross-platform, header-only, high-performance HTTP library/framework built with modern C++ (C++20).

- Repository: https://github.com/qicosmos/cinatra
- Stars: 2,185 · Forks: 392
- Language: C++
- License: MIT
- Published: 2026-10-08 · Updated: 2026-10-08 · Language: en
- Canonical page: https://hysenlabs.com/projects/qicosmos-cinatra

## Header-only, coroutine-based, and opinionated about the compiler

cinatra describes itself with five words: a unified and simple interface, header-only, cross-platform, efficient, and support for aspect-oriented programming. The first three are the load-bearing ones. Header-only means you copy an include directory into your project and there is no library to link, no package manager step, and no ABI to negotiate.

The interesting architectural choice is that the server is coroutine-based. A handler returns a lazy coroutine, so waiting on something is a co_await rather than a blocking call, and the README's basic usage example shows all three styles side by side: a plain synchronous lambda that returns immediately, a lambda returning a Lazy that ends in co_return, and a lambda that hops to a thread pool with coro_io::post for work that should not happen on the event loop. That third pattern is the one that matters in practice, and it is the difference between a framework that stalls under load and one that does not.

The cost is stated as a requirement rather than a warning. You need a C++20 compiler, listed as gcc 10.2, clang 13, Visual Studio 2022 or newer, and the build needs coroutine flags enabled explicitly.

The README is written in Chinese. There is an English version in the repository under a language directory, and the English link is at the top of the Chinese page, so the English text exists but the primary document is the Chinese one. That is worth knowing before you evaluate the project's documentation depth.

## Three CMake flags and a SIMD switch that is one-of

Because the library is header-only, integration is a matter of adding the include directory and setting compiler options. For Linux the README gives this:

```shell
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}  -pthread -std=c++20")
```

Compiling with g++ needs coroutine support enabled on top of that, plus a release-mode flag that disables tree SLP vectorization:

```shell
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines")

set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-tree-slp-vectorize")
```

The SIMD story is a compile-time choice controlled by a macro, and the README is emphatic about the constraint:

```shell
cmake -DENABLE_SIMD=SSE42 .. # 启用sse4.2指令集
cmake -DENABLE_SIMD=AVX2 .. # 启用avx2指令集
cmake -DENABLE_SIMD=AARCH64 .. # arm环境下,启用neon指令集
```

Only one may be enabled. Turning on more than one causes the build to fail. So there is no runtime CPU dispatch, which is a real difference from libraries that detect at startup. You compile for a target and your artifact stops working on a machine without those instructions. For a fleet of heterogeneous servers that is a deployment constraint to plan for, and it is the kind of thing that decides whether a framework suits your environment.

The README also asks you to confirm your CPU supports the set before enabling it, which is the correct advice given the above.

## Five lines for a server, then the API surface underneath

The smallest complete example constructs a server on a thread count and a port, registers a route for two methods, and starts it synchronously:

```c++
	#include "include/cinatra.hpp"
	using namespace cinatra;
	
	int main() {
		int max_thread_num = std::thread::hardware_concurrency();
		coro_http_server server(max_thread_num, 8080);
		server.set_http_handler<GET, POST>("/", [](coro_http_request& req, coro_http_response& res) {
			res.set_status_and_content(status_type::ok, "hello world");
		});

		server.sync_start();
		return 0;
	}
```

The route table is template-parameterised on the methods, so a handler is only invoked for requests it declared. The longer example in the README shows what else is available: reading a request body, reading header values and query parameters by name, path parameters with colon syntax such as a users and userid pattern, member function handlers bound to an object instance, and both a synchronous and an asynchronous start.

The client is part of the same library rather than a separate package. The example creates a coro_http_client, issues get and post requests against the server it just started, adds headers, passes query strings, and asserts on status codes and response bodies. Under a compile-time SSL guard, a second client makes an https request. So testing a server does not require curl or an external test harness, which removes a class of awkwardness when you are working on something that is not yet reachable from the network.

The README also documents websocket support, TLS, server-sent events in the release notes, and a proxy and redirect capable client. Servers described in the README's use cases include database access front ends, file upload and download servers, realtime push servers, and MQTT brokers built on top.

## What the release history says about direction and breakage

The releases tell a clearer story than the README's performance paragraph. Version 1.0.0 was published on 2026-04-29, after 0.9.9 on 2026-04-02 and 0.9.8 on 2026-01-13. What went into 1.0.0: a rate limiter, a multipart upload fix, a fix for retrieving the original filename when an input control was named filename, a fix adding missing information to the response status line, server-sent event support, and an upgrade of the underlying asio.

Two items in that list are worth pausing on. A rate limiter arriving at 1.0.0 tells you the project shipped HTTP serving long before anyone asked for request limiting, which is common in infrastructure libraries but means protection against abuse is something you add rather than something you inherit. The filename bug also says something about multipart form handling: a name collision between a form field called filename and the uploaded file's own name was producing the wrong original name. If you are processing uploads, that is the class of bug worth testing for.

Earlier releases point the same way. 0.9.9 changed the default so that cross-origin requests are not allowed by default, added support for dynamic files, fixed an SSL proxy issue, and fixed the Android build. A default-deny CORS change in a patch release is the kind of change that will break somebody's frontend, and it is worth reading the release notes for the version you are upgrading from rather than only the target.

0.9.8 added base64 decoding, fixed base64 decoding again in the same release, improved the SMTP client twice, and added a header operation.

## Platform coverage, and two build systems in the tree

The README leads with a platform table rather than a code sample. Ubuntu 22.04 is listed against clang 14.0.0 and gcc 11.2.0, macOS Monterey 12 against AppleClang 14, and Windows Server 2022 against MSVC 19.33. Each row carries a continuous integration badge. Note the gap between that table and the compiler requirement further down the page: the table reports what is tested, the requirement states the minimum, and they are not the same set.

The repository tree explains the rest of the layout. There is a CMakeLists.txt at the root with a cmake/ directory alongside it, a docker/ directory for container builds, tests/, and an example/ directory holding a CMakeLists.txt, a benchmark.cpp, a main.cpp and a static www/ tree. The iguana directory and the presence of .gitmodules indicate a vendored or submodule dependency, and press_tool/ looks like a separate tool rather than part of the library.

Two small inconsistencies are visible. The tree contains both a .travis.yml and a .github/ directory, while the badges on the README all point at GitHub Actions workflows, so Travis configuration appears to be a leftover. And .travis.yml alongside a codecov.yml also suggests the CI story was assembled incrementally rather than designed once.

Licensing is MIT and the LICENSE file is present at the root. There is no CLA mentioned anywhere in the README, so if you contribute, expect the standard MIT terms rather than a Google-style one, which is a rare and welcome difference for a library of this size.

## The performance claim, and what to check instead of believing it

The README states plainly that cinatra is one of the best performing HTTP servers in the world, and points at its own performance section for the numbers. Two things should be said about that sentence. First, it is a self-assessment by the project, and self-assessed performance claims are worth exactly as much as the benchmark behind them. Second, the numbers themselves live in a section of the README that a reader has to scroll to, and the harness is available: example/benchmark.cpp is in the repository tree, so the benchmark code can be read and run rather than taken on faith.

The relevant checks, then. Read example/benchmark.cpp to see what request and response shape is being timed, because a benchmark returning a fixed small body measures very differently from one serving files. Compare against the workload you actually have, since a framework's advantage usually shows up in concurrency behaviour rather than single-request latency. And keep the SIMD caveat in mind: the throughput figures depend on an instruction set you must select at compile time, so a configuration without it will not reproduce them.

There is a wiki linked from the README, including a page listing which companies use the project, which is unusual to find and a reasonable signal that this is not a hobby project. The README also links to a separate coroutine-based HTTP library document under the language directory, which is the deeper design write-up if you want to understand why the handler signature looks the way it does.

## Conclusion

cinatra fits a specific shape of work: an application that needs to speak HTTP from inside a process you already own, where adding a full framework dependency would be the decision to avoid. A single header directory, a C++20 compiler and three CMake flags is a short dependency list, and the coroutine model means a handler can await real work without a thread per connection. The constraints are equally specific. You need a compiler with coroutine support and a willing build configuration, SIMD is a single opt-in choice rather than runtime dispatch, the README is written in Chinese with only a partial English translation, and the performance claim on the README page points at a benchmark you have to run and read for yourself. Start with the CMake flags and the hello world handler, then read example/benchmark.cpp before believing any throughput figure, including the ones the README quotes.

## FAQ

### What compiler and C++ standard does cinatra require?

A C++20 compiler, listed in the README as gcc 10.2, clang 13 or Visual Studio 2022 or newer, plus the coroutine flag for g++. Because the library is header-only you set the include path yourself and compile with -pthread -std=c++20, adding -fcoroutines for g++. The platform table at the top of the README reports what continuous integration actually tests, which is a different and narrower set than the stated minimum.

### How do I enable SIMD optimisation in cinatra?

Pass a single CMake define naming the instruction set: SSE42, AVX2 or AARCH64, for example cmake -DENABLE_SIMD=AVX2 .. . Only one may be enabled at a time, and the README states that enabling more than one causes the build to fail. The choice is made at compile time with no runtime CPU detection, so confirm your target machines support the set you pick.

### Does cinatra ship an HTTP client as well as a server?

Yes. The README describes a coroutine-based HTTP and HTTPS client built on C++20 coroutines, covering ordinary get and post requests, file upload and download, websocket, redirect and proxy support. The basic usage example creates a coro_http_client, exercises get, post, custom headers, query strings and path parameters against a local server, and makes an https request behind a compile-time SSL guard.

### Is there a rate limiter or CORS handling in cinatra?

A rate limiter was added in the 1.0.0 release, published on 2026-04-29, so request limiting is something you enable rather than inherit. Cross-origin handling changed in 0.9.9, where the default became not to allow cross-origin requests. Both are recent enough that you should read the release notes for the version you are moving from rather than only the one you are moving to.

## Sources

- [Issues](https://github.com/qicosmos/cinatra/issues)
- [License: MIT](https://github.com/qicosmos/cinatra/blob/master/LICENSE)
- [qicosmos/cinatra on GitHub](https://github.com/qicosmos/cinatra)
- [README](https://github.com/qicosmos/cinatra/blob/master/README.md)
- [Releases](https://github.com/qicosmos/cinatra/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/qicosmos-cinatra
