Open-source project
rack/rack avatar
rack/rack

Rack 3.2, eleven servers, and the pieces that moved into other gems

A modular Ruby web server interface.

5,136 stars1,704 forksRubyNOASSERTION

At a glance

What is it?
Rack is the thin Ruby interface between a web server and an application: one method call, twenty middleware classes shipped in the box, and a support table that already carries end dates.
Who is it for?
Rack is the right dependency when the server is a decision you want to defer: the app returns a status, a header hash and a body, and Puma, Unicorn, Passenger or Falcon all take it as is. It is the wrong choice if you want sessions or a rackup command to arrive with the gem, because those pieces live in separate gems.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 13 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

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

From daily snapshots

Star history

  • rack/rack5,136 ★ · +4
Star history
Repository2026-09-232026-11-14
rack/rack5.1k5.1k

4 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

One method call between the server and your application

Rack sits between a web server and a web application and tries to stay as small as possible while doing it. HTTP requests and responses get wrapped in the simplest shape Ruby allows, which collapses the bridge between server, framework and application into a single method call. That call is what the Rack Specification pins down, and the contract itself lives in the repository as SPEC.rdoc.

There is no scaffold to generate. The gem goes onto the machine, or into the bundle of an existing application:

bash
# Install it generally:
$ gem install rack

# or, add it to your current application gemfile:
$ bundle add rack

Then a file named config.ru holds the whole application:

ruby
run do |env|
  [200, {}, ["Hello World"]]
end

A status, a header hash and a body array, and nothing else is asked of it. Start it with the rackup gem or another supported server, then check it from another shell:

bash
$ gem install rackup
$ rackup

# In another shell:
$ curl http://localhost:9292
Hello World

The payoff is that a return value shaped like that needs no translation per server, which is the whole reason the layer exists.

Sessions and the rackup command live in other gems

Installing rack does not install everything a Ruby web stack is assumed to include. Two pieces named in ordinary applications are deliberately separate: features from Rack::Session, and bin/rackup. Both arrive from their own gems.

bash
$ gem install rack-session rackup

The rackup gem is a generic interface for running a Rack application on a supported server, and the servers it is documented against include WEBrick, Puma and Falcon. The consequence shows up on a clean machine rather than in a test suite: an application that references Rack::Session, or a deployment script that shells out to rackup, breaks at start on a missing constant or a missing binary, because the gem you just installed was never the gem carrying those pieces. That is also why a lockfile matters here. The upgrade advice points at Rack 3.2 as the line to move to, while older lines keep receiving security patches for a while, so a lockfile can sit on a still-supported version for months and look perfectly healthy on a dependency report.

The support table is a list of deadlines

The version policy is written as a table, and one of its rows carries a date rather than an intention.

| Version | Support | |----------|---------------------------------------------| | 3.2.x | Bug fixes and security patches. | | 3.1.x | Security patches only. | | 3.0.x | End of support. | | 2.2.x | Security patches only (through April 2027). | | <= 2.1.x | End of support. |

Rack 2.2.x is in security maintenance mode with all support ending in May 2027, which means a team on that line has a dated exit rather than an open one. Rack 3.0 is the row to worry about, for two reasons at once: it is no longer supported, and it is the release with significant changes detailed in UPGRADE-GUIDE.md. A team that never took 3.0 therefore has both a dead support line and a migration document waiting.

The release tags tell a consistent story: v3.2.6 on 2026-04-01, v3.2.4 on 2025-11-10, and a v3.0.9.1 patch tag from 2024-02-21 that landed on a line the table already calls end of support. The last push to the default branch main was on 2026-10-31. Read the two together, because commit activity on main says nothing about whether the version your lockfile pins is still being patched.

Ruby 3.4 removes base64 from the default gems

One dependency note in this project is easy to skim and expensive to debug. Starting in Ruby 3.4 the base64 dependency is no longer a default gem, which can produce a warning or an error saying base64 is missing. The correction is one line of intent: add base64 as a dependency to the project.

It belongs in an evaluation of Rack because the symptom arrives at runtime, in an application that never asked about base64, and it arrives on a Ruby upgrade rather than on a gem upgrade. The person bumping Ruby is often not the person who assembled the dependency list. The fix belongs in the Gemfile.lock that gets regenerated at that moment, and any application that resolves Rack but not base64 is one Ruby minor version away from a broken endpoint.

Twenty middleware classes ship with the gem

The bundle carries a full set of optional middleware, and each one changes one narrow thing in the request or the response. `Rack::CommonLogger` writes Apache style logfiles. `Rack::ConditionalGet` returns Not Modified responses when the response has not changed. `Rack::Config` modifies the environment before the request is processed, and `Rack::MethodOverride` modifies the request method based on a submitted parameter. `Rack::Events` provides hooks when a request is received and when the response is sent. `Rack::Head` returns an empty body for HEAD requests, `Rack::Lock` serializes requests using a mutex, and `Rack::Reloader` reloads files if they have been modified.

On the response side, `Rack::ContentLength` sets a content-length header based on body size, `Rack::ContentType` sets a default content-type header, `Rack::Deflater` compresses responses with gzip, and `Rack::ETag` sets an etag header on bodies that can be buffered. `Rack::Runtime` adds a response header with the time taken to process the request. `Rack::Sendfile` works with web servers that can use optimized file serving for file system paths, `Rack::Static` handles configurable static files, and `Rack::TempfileReaper` removes temporary files created during a request.

Three of these deserve a second look before they go into a stack. `Rack::Lock` serializes requests, so a global mutex in front of the app trades concurrency for simplicity and the effect is per process, which makes a multi worker deployment behave differently from a single process one. `Rack::MethodOverride` takes the verb of a request from a parameter rather than from the transport, so code that treats the verb as a security signal has to know where it came from. And `Rack::ShowException` with a clickable backtrace, `Rack::ShowStatus` for empty client error responses, and `Rack::Lint` for checking conformance to the Rack Specification are all instruments you switch on deliberately, since the last one is the piece that catches an app that quietly stopped conforming.

The same app across twenty servers, with one caveat stated plainly

The servers named as supported are Agoo, Falcon, Iodine, NGINX Unit, Phusion Passenger, Pitchfork, Puma, Thin, Unicorn, uWSGI, and Lamby for AWS Lambda. Passenger is described as the mod_rack for Apache and for nginx, which makes it the odd one out: it arrives as a module inside an existing web server rather than as a standalone Ruby process you start.

The portability claim is stated without hedging: in general, any valid Rack app will run the same on all of these servers, without changing anything. The next sentence qualifies it by telling you to consult the server documentation for the features and limitations each one may have. That caveat is not decorative, and the middleware list shows exactly where it bites. `Rack::Sendfile` only does its job with servers that can use optimized file serving for file system paths, so the same middleware is a fast path on one server and a no-op on another. `Rack::Deflater` compresses responses with gzip, which changes the bytes on the wire and leaves the content-length question to the body it compresses. Treat the portable part as the request and response shape, and treat everything above it as something to check per server.

MockRequest replaces the socket in a test suite

Four helper interfaces cover the plumbing that would otherwise be duplicated in every project. `Rack::Request` adds query string parsing and multipart handling to the raw environment hash. `Rack::Response` handles convenient generation of HTTP replies and cookie handling. `Rack::MockRequest` and `Rack::MockResponse` exist for testing a Rack application without real HTTP round-trips, and that pair is the one that changes how fast a suite runs. `Rack::Cascade` tries additional Rack applications when an application returns a not found or method not allowed result, so a chain of small apps can answer from the first one that has an opinion.

The repository layout mirrors that split. lib/ holds the code, test/ plus a test.rb entry point hold the suite, and config/, contrib/ and docs/ sit beside them. At the top level you find SPEC.rdoc for the contract, CHANGELOG.md, UPGRADE-GUIDE.md, SECURITY.md, CONTRIBUTING.md and GETTING-STARTED.md, along with rack.gemspec, Gemfile, Rakefile, an .editorconfig, a .rubocop.yml for the linter, .yardopts for documentation, an .mailmap and an MIT-LICENSE file.

The specification document, SPEC.rdoc, is where the contract is written down precisely.

Editorial conclusion

Rack is the right dependency when the server is a decision you want to defer: the app returns a status, a header hash and a body, and Puma, Unicorn, Passenger or Falcon all take it as is. It is the wrong choice if you want sessions or a rackup command to arrive with the gem, because those pieces live in separate gems. Before locking a version, read the support table against your release line, since 3.0.x is already end of support and 2.2.x stops receiving security patches in 2027, and add base64 to the bundle before the next Ruby 3.4 upgrade turns a runtime import into an error.

Frequently asked questions

Which Rack version should a new Ruby application use?

Rack 3.2. The support table gives 3.2.x bug fixes and security patches, gives 3.1.x security patches only, and marks 3.0.x as end of support. Projects on Rack 2.2.x are asked to upgrade to Rack 3.2, since 2.2.x support ends in May 2027.

Do sessions and the rackup command need separate gems?

Yes. Features from Rack::Session and the bin/rackup command come from other gems, and the installation note gives gem install rack-session rackup as the fix. The rackup gem is the generic interface for running a Rack application on a supported server such as WEBrick, Puma or Falcon.

Why does base64 go missing after a Ruby upgrade?

Starting in Ruby 3.4 the base64 dependency is no longer a default gem, which can cause a warning or an error saying base64 is missing. The stated correction is to add base64 as a dependency to your project.

How do I check that an application conforms to the Rack Specification?

Use Rack::Lint, which ships with Rack and checks conformance to the Rack Specification. The specification itself is kept in the repository as SPEC.rdoc, and CHANGELOG.md plus UPGRADE-GUIDE.md cover changes between versions.

Will the same Rack app run unchanged on Puma and Phusion Passenger?

The project states that any valid Rack app will run the same on all the listed servers without changing anything, and it also says to consult each server documentation for its own features and limitations. Passenger is described as the mod_rack for Apache and for nginx, so it runs as a module inside those servers.

Official sources

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

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/rack-rack.svg)](https://hysenlabs.com/projects/rack-rack)

Was this useful?