# ocaml-cohttp: one HTTP parser for Lwt, Async, Eio and Js_of_ocaml

> ocaml-cohttp is an OCaml library for HTTP clients and servers that keeps one portable HTTP parser and puts a thin backend under it for each concurrency model, with Lwt, Async, Eio, Js_of_ocaml, libcurl and Mirage variants. It is aimed at OCaml services that need HTTP without committing to a runtime.

**mirage/ocaml-cohttp** — An OCaml library for HTTP clients and servers using Lwt or Async

- Repository: https://github.com/mirage/ocaml-cohttp
- Stars: 779 · Forks: 187
- Language: OCaml
- License: NOASSERTION
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/mirage-ocaml-cohttp

## A portable parser with interchangeable backends

ocaml-cohttp is an OCaml library for HTTP clients and servers using Lwt or Async, and the README describes it more precisely as a library for creating HTTP daemons with a portable HTTP parser and implementations using various asynchronous programming libraries.

The architecture is the interesting part and it explains the package explosion. At the bottom sits Http, which provides the essential type definitions used in Cohttp and an extremely fast HTTP parser. It is designed to have no dependencies, so that other packages can interoperate with Cohttp easily.

Above that parser sit the backends, one per concurrency model, each implementing the same IO signature. The README says you can implement other targets using the parser very easily, and points at the IO signature in lib/s.mli to implement in the desired backend. That is the key design decision: the protocol handling is written once, and the scheduling model is a thin layer underneath.

The audience is OCaml developers who have to speak HTTP and do not want their HTTP library to dictate their concurrency model. It is a protocol library rather than a web framework, and the table of contents reflects that: client tutorials, timeouts, sessions, multipart form data, a custom resolver example, redirects, a basic server tutorial, installed binaries and debugging.

## The package map and what each backend is for

The list of packages is the quickest way to understand the scope, and each one exists because of a specific environment.

Cohttp_lwt_unix uses Lwt with the UNIX bindings, with ocaml-tls as the TLS implementation for HTTPS. Cohttp_async uses Async with async_ssl for HTTPS. Cohttp_lwt exposes an OS independent Lwt interface, and it is the one the Mirage interface uses to generate standalone microkernels, through the cohttp-mirage subpackage.

Three of them are for unusual targets. Cohttp_lwt_jsoo compiles to a JavaScript module that maps the Cohttp calls to XMLHTTPRequests, and the README says it is used to compile OCaml libraries like the GitHub bindings to JavaScript and still run efficiently. Cohttp_eio uses eio to take advantage of new features from multicore OCaml 5.0. Cohttp_curl uses libcurl through ocurl as its backend, and it comes with an Lwt variant and an Async variant.

Finally, Cohttp_server_lwt_unix uses Lwt to implement a more efficient web server with a minimal interface, which is the piece to reach for when you want a server rather than a client.

The practical reading is that you install the backend that matches the runtime you already have. Installing several means installing several TLS stacks.

## Installing from opam, and following the development branch

The stable release comes from opam, and the README is explicit that you should install the backends you actually want rather than the umbrella package:

```bash
$ opam install cohttp-lwt-unix cohttp-async
```

That is the whole installation story for the released version. The development release is a pin rather than a version, so the tree moves with the branch:

```bash
$ opam pin add cohttp --dev-repo
```

The repository is a large set of opam packages, with each backend having a package, a template file for the template based workflow, and a source directory, and there is a separate cohttp-bench package and a test_helpers directory. The Makefile shows the developer commands: build runs dune build, test runs dune runtest, js-test builds the JavaScript test target, fmt runs the dune formatter in auto-promote mode, and there are dedicated eio targets including a nix develop shell for eio development.

For the OCaml 5 direction there is a separate flake, flake.nix with a lock file, so a developer can get the eio environment without assembling it by hand.

## A client in OCaml: the tuple response and the event loop

The client tutorial is written against the Lwt client, with a note that it should be easily translatable to Async, and the example downloads the Reddit frontpage.

The shape of the API is what matters. The client methods live in Cohttp_lwt_unix.Client, where call is the most general and there are specialised methods such as get and post. A call takes a Uri.t and makes the request, and it accepts optional arguments for things like header information. The response comes back as a tuple whose first element carries the status code, the headers and the HTTP version, and whose second element is the body.

The body then has to be consumed explicitly. The README points out that Cohttp_lwt.Body.to_string is what converts it, and that it is up to the caller to keep a reference to the result.

The last piece of the puzzle is the event loop. Nothing happens until Lwt_main.run is called, which runs the loop and returns the final value of the body, which is then printed. The tutorial opens two modules, Cohttp for the backend independent pieces and Cohttp_lwt_unix for the Lwt plus UNIX specific ones, and that split is the shape of the whole library.

The tutorial also shows how to build and run the example with dune, by writing a dune file with an executable named client_example linked against cohttp-lwt-unix, and then running the built binary.

## HTTPS defaults and the CONDUIT_TLS switch

Both Cohttp_lwt_unix and Cohttp_async are able to request an HTTPS page by default, and the way that is achieved differs between them in a way that will surprise you if you switch backends.

For Cohttp_lwt_unix, users can use ocaml-tls by installing tls-lwt, or ocaml-ssl by installing lwt_ssl. The README says the latter is the default if both are installed, and that it is possible to force the selection of tls with the environment variable CONDUIT_TLS=native.

For Cohttp_async, the default is to use async_ssl, but users are able to use ocaml-tls with some modifications, and the wording of that clause is a warning in itself.

The practical consequence is that a project that migrates between concurrency models may end up with a different TLS stack and therefore a different set of supported cipher suites and a different certificate store, without anything in the application code changing.

The reference modules for the client API are given in the README, Cohttp_lwt.Client and Cohttp_async.Client, and the full documentation for the latest published version is on the repository's GitHub pages.

## Timeouts, sessions, redirects and multipart bodies

The tutorial sections cover the parts of HTTP that applications get wrong, and the timeout example is the most instructive because it shows the shape of the problem.

There is no built in timeout parameter on the request, so the README shows how to build one with Lwt.pick, racing the request against a Lwt_unix.sleep and returning a variant that distinguishes Done from Timeout. The example modifies the get call to compute with a time of 0.1 seconds, and running the compiled example most likely fails with the output Fatal error: exception (Failure "Timeout expired"). The README notes that in the case of cohttp-async you can directly use Async's with_timeout instead.

The remaining sections cover managing sessions, multipart form data, dealing with redirects, and creating a custom resolver, with the Docker Socket Client given as the worked example of the last one. The custom resolver example is the one to read if you are moving off the default host resolution, because it shows the seam where the library lets you substitute your own behaviour.

The Basic Server Tutorial covers the server side, and the Installed Binaries and Debugging sections tell you what the package installs and how to get visibility into what it is doing.

## Where ocaml-cohttp is the wrong tool

Three cases point elsewhere.

The first is a non-OCaml stack. This is an OCaml library with an opam package per backend, and the value of the design, one parser with interchangeable backends, only pays off if your code is in OCaml.

The second is a project that wants a web framework. Cohttp gives you a parser, a client and a server, and the table of contents says so. There is no routing layer, no middleware pipeline, no template engine and no form framework here. The server package is described as a minimal interface, which is exactly what it is, so a team expecting a framework should look elsewhere and use this underneath.

The third is a project that wants one dependency instead of a family. The backend split is the feature, and it is also the cost. Installing cohttp-lwt-unix brings the Lwt stack and a TLS library, and adding cohttp-async brings another TLS library, and cohttp-curl adds libcurl through ocurl, and cohttp-eio pulls in a different concurrency runtime. The README itself advises installing the specific backends you want, which is the right advice, but it means the dependency count is a design decision you make rather than one you inherit.

One more practical point for anyone starting: the project moves through six minor versions in a year, and the API changes with it. The CHANGES.md file is in the repository, and reading it before a major upgrade is cheaper than debugging a changed signature.

## Conclusion

Adopt ocaml-cohttp if you are writing an OCaml service or client and want one HTTP API that follows whichever concurrency model the rest of your code uses, or if you are targeting Mirage unikernels or compiling OCaml to JavaScript. Do not adopt it if your stack is not OCaml, and do not expect a web framework, since this is a protocol library with a parser and a set of backends. Verify three things first: which backend your project already uses, because the packages are separate and installing more than one adds a TLS stack you did not ask for; which TLS implementation you want, since the default differs between Lwt and Async and the CONDUIT_TLS=native environment variable changes the choice; and that the API you need is present in the version you install, since the package set is versioned together and moves often. The licence is recorded in a custom LICENSE.md to review, the last push was on 2026-09-08, and the newest release is v6.3.0 dated 2026-08-20.

## FAQ

### What is ocaml-cohttp?

It is an OCaml library for creating HTTP daemons, with a portable HTTP parser and implementations for several asynchronous programming libraries. It provides clients and servers for Lwt, Async, Eio, Js_of_ocaml and Mirage, plus a libcurl backend.

### How do I install ocaml-cohttp and the backend I need?

Install the stable version from opam with the packages for the backends you want, for example opam install cohttp-lwt-unix cohttp-async. For the development release use opam pin add cohttp --dev-repo, and the repository also ships a flake for the eio development shell.

### How does the ocaml-cohttp client return a response?

The response is a tuple whose first element holds the status code, headers and HTTP version, and whose second element is the body, which you convert with Cohttp_lwt.Body.to_string. You must run the Lwt event loop with Lwt_main.run for the request to execute.

### How do I set a timeout on an ocaml-cohttp request?

There is no built in timeout parameter, so the README shows how to build one with Lwt.pick, racing the request against Lwt_unix.sleep and returning a Done or Timeout variant, and then failing with the message Timeout expired. With cohttp-async you can use Async's with_timeout directly.

### Which TLS implementation does ocaml-cohttp use?

Cohttp_lwt_unix can use ocaml-tls by installing tls-lwt or ocaml-ssl by installing lwt_ssl, with the latter as the default when both are present and tls selectable through the CONDUIT_TLS=native environment variable. Cohttp_async defaults to async_ssl, with ocaml-tls possible with some modifications.

## Sources

- [Issues](https://github.com/mirage/ocaml-cohttp/issues)
- [mirage/ocaml-cohttp on GitHub](https://github.com/mirage/ocaml-cohttp)
- [README](https://github.com/mirage/ocaml-cohttp/blob/main/README.md)
- [Releases](https://github.com/mirage/ocaml-cohttp/releases)

---

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