Library / SDK
ring-clojure/ring avatar
ring-clojure/ring

Ring: the WSGI-style abstraction that keeps Clojure web apps portable

Clojure HTTP server abstraction

3,886 stars528 forksClojureMIT

At a glance

What is it?
Nine libraries, one handler interface, and two servlet generations to support. Ring is a small spec with a long life, and the interesting design decisions are in what it refuses to include.
Who is it for?
Ring's real product is the SPEC.md file at the root, not any of the nine libraries. Everything else in the repository is either an implementation of that spec, a protocol subset of it, or a way to cross into a JVM web server, which means the interesting decisions were made once and the code since then has been maintenance.
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 10 days ago.
What is it written in?
Mainly Clojure, according to GitHub's language statistics.

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

Editorial analysis

One handler interface, written down once in SPEC.md

Ring describes itself as a Clojure web applications library inspired by Python's WSGI and Ruby's Rack. The pitch in the README is that by abstracting the details of HTTP into a simple, unified API, web applications can be built from modular components shared among applications, web servers, and web frameworks.

That is the whole idea, and it is borrowed deliberately. The README's Thanks section says the project borrows heavily from Ruby's Rack and Python's WSGI and thanks both communities, which matters because it means Ring's design has already survived two rounds of scrutiny elsewhere. A handler that satisfies the spec can sit behind Jetty through one adapter or behind a servlet container through another, and application code above it does not change.

The specification itself is a file, not a library. The README points at SPEC.md at the root of the distribution as the complete description of the Ring interface, and sends anything about how to use Ring to the wiki. There is also a generated API documentation site, which is the place to go for function-level detail rather than the interface shape.

This split between spec, wiki, and API docs is the practical thing to know before reading the code. A tiny core library with a large surface of documentation usually means the library is stable and the decisions are in the spec document.

Nine libraries split by what you actually need to run

The Libraries section is the most useful part of the README, and the splits are finer than most Clojure libraries would bother with. `ring/ring` is a meta-package holding all relevant dependencies. `ring/ring-core` carries core functions and middleware for handlers, requests, and responses. `org.ring-clojure/ring-core-protocols` contains only the protocols needed for building responses, and `org.ring-clojure/ring-websocket-protocols` contains only the protocols needed for websockets.

Those last two are the design statement worth reading twice. Ring deliberately split the response-building protocols into their own library so that a websocket-only or a minimal consumer does not pull the whole core. That kind of split is not an accident of packaging; it is the project saying the protocol surface is the stable part and the middleware stack is the optional part.

`ring/ring-devel` is for developing and debugging Ring applications. `ring/ring-jetty-adapter` is the adapter that uses an embedded Jetty web server. The other two handle servlet interop, and the way they are split sets the tone of the whole project:

clojure
ring/ring-servlet - construct legacy Java Servlets (≤ 4.0) from Ring handlers
org.ring-clojure/ring-jakarta-servlet - construct Jakarta Servlets (≥ 5.0)

Two separate artifacts for the servlet boundary, one per servlet generation, is what careful long-lived library maintenance looks like. The README links the Jakarta Servlet project for the 5.0 and later line and Jetty for the embedded server.

Topics on the repository are clojure, http, ring, and web, which is about as plain as topic lists get.

Installing a single sublibrary rather than the meta-package

The install instructions name a specific library, ring-core, and give both build tool forms. For a deps.edn project:

clojure
    ring/ring-core {:mvn/version "1.15.5"}

And for Leiningen, the same coordinate in vector form:

clojure
    [ring/ring-core "1.15.5"]

Note that the README picks a sublibrary for the example rather than the `ring/ring` meta-package. That is consistent with how the Libraries section is written and with the protocols split: the project expects you to choose the narrowest artifact your application needs. Pulling `ring/ring` is reasonable for a new project, but the documented path is deliberately more selective.

The repository layout supports both toolchains. There is a project.clj for Leiningen and a checkouts/ directory, which is a Leiningen mechanism for pointing at local dependency checkouts and a common convenience for working on Ring's own libraries together. The scripts side of the repo is update_versions.bb, a babashka script, which suggests version bumps across the nine libraries are automated rather than done by hand nine times.

The linting setup is .clj-kondo/ at the root, so the project is checked with clj-kondo, and ring-bench/ holds benchmarks as a first-class part of the distribution rather than something kept elsewhere.

Where releases are published, and where they are not

Here is a discrepancy worth naming, because it changes where you look for version history. The repository has no published GitHub releases at all. The README's Documentation section points at the CHANGELOG.md in the repository root as the first link under Documentation, before the wiki and the API docs.

So the authoritative record of what changed in 1.15.5 or in whatever came before it lives in a file on the master branch rather than in a release feed. For a library that is still being worked on, with a last push on 2026-09-06 and no archived flag, that is a workable arrangement, but it means dependency updates are not discoverable through GitHub's releases page or through most tooling that watches for tagged releases.

The version number itself appears in exactly one place in the README, inside the two installation snippets. GitHub reports the license as MIT and the README's License section agrees, releasing under the MIT license with copyright held by Mark McGranaghan, James Reeves and contributors across 2009 to 2026. So there is no license conflict to resolve here, which is worth saying because it is not always true of long-lived Clojure libraries.

GitHub reports 3,883 stars, 528 forks and 41 open issues, in a Clojure project on master. The issue count is modest for a project this old, which suggests the low ceremony path of spec plus wiki plus changelog is doing its job.

A description that undersells what the project is

GitHub's short description for the repository is Clojure HTTP server abstraction. The README describes it as a Clojure web applications library. Those are different claims, and the README's is the accurate one.

Ring is not a server. It ships no HTTP server of its own; the only server in the list is ring-jetty-adapter, which wraps an embedded Jetty. What Ring provides is an interface and the middleware that most Clojure web applications are built from. If you read the description and expect something you can start and listen on, the one adapter library will look too small to justify the ecosystem.

That framing also tells you where Ring sits relative to the rest of the Clojure web stack. Frameworks such as compojure and rendering approaches such as hiccup, both of which turn up in search results for Clojure web work, sit above Ring: they produce handlers and Ring runs them. The related searches for this project return Ring-Jetty adapter references, ring-defaults, and Ring json as well, which is a fair picture of what people are actually assembling around it.

None of this is a criticism so much as a reading guide. The abstraction is intentionally thin, which is why the protocols split works and why the library has stayed stable across a very long run.

How to start reading this repository

If you are evaluating Ring for a Clojure web application, the order that works is SPEC.md, then ring-core's middleware, then the wiki. The spec tells you the shape of a handler and what a request and a response are. The middleware is what you will actually compose. The wiki covers the how-to material the README explicitly declines to.

Two things are worth deciding early. The first is whether you use the Jetty adapter and its embedded server, which is the frictionless path, or whether you deploy into an existing servlet container, which is what ring-servlet and ring-jakarta-servlet exist for. The second is whether you need the websocket protocols, and if you do, whether that changes your servlet generation, since Jakarta 5.0 and later is a separate artifact from the legacy servlet line.

The other thing to keep in mind is that Ring is a compatibility layer over JVM web infrastructure, so its ceiling is that infrastructure's. When the servlet spec moved from 4.0 to 5.0, this project absorbed that by shipping two libraries instead of one, and it will absorb the next change the same way. That is the trade you are making: less control over the HTTP details in exchange for handlers that keep working as the JVM moves underneath them.

For the contribution side, the README asks that CONTRIBUTING.md be read before a pull request is submitted, and the CI badge at the top of the README points at a GitHub Actions test workflow.

Editorial conclusion

Ring's real product is the SPEC.md file at the root, not any of the nine libraries. Everything else in the repository is either an implementation of that spec, a protocol subset of it, or a way to cross into a JVM web server, which means the interesting decisions were made once and the code since then has been maintenance. The abstraction earns its keep when you want a Clojure handler to run on Jetty today and on Jakarta Servlet in a framework later, and it costs you when you need streaming bodies, websockets, or async handlers that the handler shape does not naturally express, which is presumably why the websocket protocols live in a separate library instead of the core. Version 1.15.5 is the coordinate to depend on, the CHANGELOG.md at the root is where change history actually lives, and the wiki is where anything the README does not cover will be.

Frequently asked questions

What is Ring used for in a Clojure web application?

Ring is an HTTP abstraction layer, not a web server. Applications write handlers that satisfy the interface described in SPEC.md, and those handlers can then run behind Jetty through ring-jetty-adapter or inside any servlet container through ring-servlet or ring-jakarta-servlet. Web frameworks and routing libraries such as compojure produce handlers that Ring runs.

Which Ring artifact should a new project depend on?

A new application can depend on the ring/ring meta-package, which contains all relevant dependencies. The README's own installation example is narrower, using ring-core at version 1.15.5, which fits the library's design of splitting out what you do not need, including response protocols and websocket protocols as separate artifacts.

What is the difference between ring-servlet and ring-jakarta-servlet?

They target different generations of the Java servlet specification. ring-servlet constructs legacy Java Servlets of version 4.0 or earlier from Ring handlers, while org.ring-clojure/ring-jakarta-servlet does the same for Jakarta Servlets of version 5.0 or later. Both exist so a Ring handler can be deployed into older and newer containers.

How do I see what changed in a new Ring version?

The repository publishes no GitHub releases, so the version history lives in the CHANGELOG.md file at the root of the master branch, which the README lists as the first documentation link. The README itself carries the current coordinate, ring/ring-core at version 1.15.5, in its installation examples.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. ring-clojure/ring on GitHub
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/ring-clojure-ring.svg)](https://hysenlabs.com/projects/ring-clojure-ring)