Open-source project
http-kit/http-kit avatar
http-kit/http-kit

http-kit: a Ring-compatible HTTP client and server for Clojure

Simple, high-performance event-driven HTTP client+server for Clojure

2,570 stars353 forksJavaApache-2.0

At a glance

What is it?
http-kit is a small, dependency-free HTTP client and server for Clojure that runs on an event-driven loop. It fits Ring middleware, WebSockets and high-connection-count services, and it asks you to put a reverse proxy in front of it in production.
Who is it for?
Adopt http-kit when you want a Ring-compatible server or client whose entire jar is about 90kB with zero dependencies, and when your concurrency comes from many open connections rather than from CPU-heavy handlers. Do not adopt it if you need the servlet container ecosystem, JSR-356 WebSocket containers, or a server you can expose directly to the internet without a proxy.
Can I use it commercially?
Yes. Apache-2.0 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 41 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What http-kit replaces in a Clojure stack

A Clojure web service usually needs two pieces: something that accepts connections and turns them into Ring requests, and something that makes outbound calls. The default answer for the first piece is a Jetty adapter, which brings a servlet container and its thread pool along with it. http-kit occupies both positions with one library. The README calls it a "drop-in replacement for the standard Ring Jetty adapter", which is the claim that matters: existing middleware and routing libraries keep working because the request and response maps are the same shape.

The second position is the client. http-kit ships a client in the same jar as the server, so a service that calls three other services does not add a second HTTP stack. The README states the client and server together are about 90kB with zero dependencies, and roughly 3k lines of code. That number is the whole pitch. A Clojure project that wants a small dependency tree, or that ships as a GraalVM native image, has a reason to care about a jar that pulls nothing behind it.

The audience is therefore narrow and specific: Clojure developers building services where connection count matters more than per-request CPU, and teams already committed to Ring who want to change the adapter without changing their application code.

The event-driven loop and what it costs you

http-kit uses an event-driven architecture, the same family of design as nginx, rather than one thread per connection. The README states RAM usage is O(n) with only a few kB per connection, and links to a write-up about serving more than 600k concurrent connections. The distinction from a thread-per-request server is not cosmetic. With a thread pool, each idle keep-alive connection holds a thread, and threads are the expensive resource. With an event loop, an idle connection holds a socket and a small amount of bookkeeping.

That design has a cost that the README does not dwell on. Work that blocks occupies the loop. A handler that does a synchronous database call, reads a file, or waits on a lock stops other connections on that loop from making progress. The library gives you both styles, described as "sync or async", and says the API lets you mix and match. Mixing is the correct move, but it is a decision you have to make per handler, and nothing in the library enforces it. If your application is a handful of endpoints that each spend 200ms in a database query, the event loop buys you very little and a thread-per-request server is easier to reason about.

WebSockets and long-polling come from the same unified API, which is where the architecture pays off. A server holding tens of thousands of open WebSocket connections is exactly the workload an event loop is built for, and exactly the workload a thread pool handles badly.

Adding http-kit and serving a first request

The project is published on Clojars, and the README points at cljdoc for the API reference and the GitHub wiki for getting started. The dependency coordinate is http-kit/http-kit. The README lists v2.8.1 and v2.9.0-beta4 as the latest releases, so 2.8.1 is the stable choice and the beta is the one to avoid unless you are deliberately tracking it. The README does not print a deps.edn or project.clj snippet, so add the coordinate in whichever of those two files your project uses, under :deps for deps.edn and under :dependencies for Leiningen, and check cljdoc for the current version string rather than copying one from an article.

The README also does not print a server or client example. The namespaces and call shapes are documented on the wiki and cljdoc, and the README's own description of the API is what you should read first: it advertises a simple unified API for WebSocket and HTTP long-polling and streaming, and says the same API lets you mix synchronous and asynchronous handling. Start from the wiki's getting-started page, which is the entry point the README links, and confirm the adapter namespace there before you write any handler code.

What you should expect once a handler is registered: because the adapter is Ring-compatible, that handler can be wrapped in your existing middleware without changes. That is the property the whole migration rests on, and it is worth testing with one real endpoint from your application before you move the rest.

Production means a reverse proxy in front

The README is unusually direct here: for production environments it "strongly recommended" to run http-kit behind a well-configured reverse proxy, and names nginx, Caddy, HAProxy and AWS ALB as examples, with a wiki page on production environments. This is not a formality. An event-driven server written for concurrency is not automatically hardened for the open internet, and the project is telling you that the hardening layer is somebody else's battle-tested software.

Treat that as a real constraint on adoption rather than a footnote. If your deployment target is a platform that hands you a port and expects the application to be the edge, you are adding a component the platform may not provide. If your deployment already sits behind a load balancer, you are already doing what the project asks and the recommendation costs you nothing.

The second thing to verify is the release line. The most recent releases listed are v2.9.0-beta4 from 2026-07-31, v2.9.0-beta3 from 2025-11-04, and v2.9.0-beta2 from 2025-08-19. That is a beta series that has been running for over a year, with v2.8.1 as the stable tag alongside it. Nothing is broken by that, but a team that pins to a beta to get a fix should know it is pinning to a beta. The last push to the repository was on 2026-08-21, so the project is not dormant, but the release cadence and the commit cadence are different things and the changelog is where the difference shows up.

http-kit against the Jetty adapter

The honest alternative for most Clojure teams is not another small HTTP library, it is the Jetty adapter they already have. Jetty is a servlet container. It gives you the servlet specification, a large ecosystem of filters and integrations, mature TLS and HTTP/2 handling, and a thread pool whose behavior is documented in enormous detail by people who have run it at scale for two decades. When something goes wrong at 3am, that documentation exists.

http-kit gives up that ecosystem in exchange for a small dependency footprint and a connection model that scales with open sockets instead of threads. The trade is real in both directions. A team that needs a servlet filter for Kerberos, or that runs inside an application server, cannot swap the adapter and keep those pieces. A team shipping a native binary, or one that is tired of a dependency tree that grows every time a transitive library updates, gets a concrete benefit from a jar with zero dependencies.

The comparison is not about which is faster in the abstract. The README links a benchmark suite and explicitly warns readers to be skeptical of benchmarks and check the context, which is the right posture. Run the project's benchmark suite in your own environment against your own handler before you treat a throughput number as a reason to migrate.

Licence and the cost of staying current

http-kit is licensed under Apache 2.0, and the README carries a copyright line covering 2012-2025 for Feng Shen and contributors, with the full text in LICENSE.txt. Apache 2.0 is a permissive licence with an explicit patent grant and a requirement to preserve notices. That makes it straightforward for commercial use and for redistribution inside a larger product. It is not a copyleft licence, so it does not reach into your application code. None of this is legal advice, and if you are redistributing the library as part of a product, the notice obligations in LICENSE.txt are worth reading directly rather than taking a summary.

The maintenance model is community-owned. The README states the project was created by @shenfeng and is "currently being maintained by its community", with an open invitation for additional contributors. That is a meaningful signal about upgrade cost: there is no vendor with a support contract, and the direction of the project is set by whoever shows up. The practical consequence is that you should read CHANGELOG.md before bumping a version rather than assuming a patch release is inert, and you should be prepared to read the source, which at roughly 3k lines is a realistic thing to do.

A migration from the Jetty adapter is not a rewrite, but it is also not zero work. The adapter path changes, the server options change, and any code that reached into Jetty-specific configuration has to be rewritten. Budget for that rather than assuming the drop-in claim covers every line of your deployment configuration.

Editorial conclusion

Adopt http-kit when you want a Ring-compatible server or client whose entire jar is about 90kB with zero dependencies, and when your concurrency comes from many open connections rather than from CPU-heavy handlers. Do not adopt it if you need the servlet container ecosystem, JSR-356 WebSocket containers, or a server you can expose directly to the internet without a proxy. Before committing, check two things yourself: that the Ring adapter path you use is covered by the current tests in the repository, and that the release you pick is the one you intend, since the newest published version listed is v2.9.0-beta4 rather than a stable tag.

Frequently asked questions

What is http-kit used for in a Clojure project?

It provides both the HTTP server and the HTTP client. The server is a Ring-compatible adapter, so it can replace the standard Jetty adapter, and the client makes outbound requests from the same jar.

How do I install http-kit?

It is published on Clojars under the coordinate http-kit/http-kit. The README lists v2.8.1 and v2.9.0-beta4 as the latest releases, and the API reference is on cljdoc.

Does http-kit support WebSockets?

Yes. The README describes a unified API for WebSocket and HTTP long-polling and streaming, and lists websockets among the project's topics. The namespace used for WebSocket handlers is not shown in the README, so check the wiki or cljdoc.

Is http-kit a replacement for Jetty in a Ring application?

The README calls it a drop-in replacement for the standard Ring Jetty adapter, so existing middleware and libraries keep working. The difference is the architecture: http-kit is event-driven rather than thread-per-request, and the README recommends running it behind a reverse proxy in production.

Can http-kit be exposed directly to the internet?

The README strongly recommends running it behind a well-configured reverse proxy such as nginx, Caddy, HAProxy or AWS ALB in production environments, and points to a wiki page on production environments for details.

Official sources

  1. http-kit/http-kit on GitHub
  2. License: Apache-2.0
  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/http-kit-http-kit.svg)](https://hysenlabs.com/projects/http-kit-http-kit)