---
title: "Rackとは？Rubyのサーバーとアプリをつなぐ仕様とミドルウェアの書き方【Rack 3.2】"
url: "https://www.issoh.co.jp/tech/details/5598/"
published: 2025-02-28
updated: 2026-09-30
categories: ["Ruby"]
publisher: "株式会社一創"
---

# Rackとは？Rubyのサーバーとアプリをつなぐ仕様とミドルウェアの書き方【Rack 3.2】

Rack（ラック）は、RubyのWebサーバーとWebアプリケーションのあいだの受け渡し方を決めた仕様と、その仕様を実装したgemの名前です。Rails、Sinatra、Hanami、Rodaはどれも Rack アプリケーションとして動き、Puma や Unicorn はどれも Rack アプリケーションを動かせます。この記事では、仕様の中身（`call` と3要素の配列、env）、`config.ru` と rackup、ミドルウェアの書き方、Rails との関係、Rack 2 から 3 で変わった点を、2026年9月時点の最新版 Rack 3.2.7 で実際に動かした結果とともに説明します。

なお、サーバーを収める19インチラックなどの「ラック」とは別物です。この記事が扱うのは Ruby の Rack です。

## まとめ：Rackの仕様・最新版・Rack 3の要点

- **定義**：`call(env)` に応答し、`[ステータス, ヘッダ, ボディ]` の配列を返すオブジェクトが Rack アプリケーション。サーバーはこの形だけを前提に呼び出す
- **最新版**：rack 3.2.7（2026-08-13）。3.1系はセキュリティ修正のみ、3.0系はサポート終了、2.2系はセキュリティ修正のみ
- **Rack 3 の必須対応**：レスポンスヘッダ名は小文字（`content-type`）。`Content-Type` と書くと `Rack::Lint` が例外を送出し、既定の rackup 開発環境では500応答になる
- **rackup は別gem**：Rack 3.0 で `rackup` コマンドと `Rack::Handler` が rack から切り出された。本番は `puma` などサーバーのコマンドで直接起動するのが推奨
- **ミドルウェア**：`@app.call(env)` の前後に処理を挟むクラス。ボディに対して `each` を直接呼ぶのは Rack 3 の仕様違反
- **Rails との関係**：Rails アプリ自体が1つの Rack アプリケーションで、`bin/rails middleware` で積まれたミドルウェアを確認できる

## Rackアプリケーションの定義：callと3要素の配列

Rack の仕様書（[SPEC.rdoc](https://github.com/rack/rack/blob/main/SPEC.rdoc)）は、Rack アプリケーションを「`call` に応答する Ruby オブジェクト」と定義しています。`call` は引数を1つ（env＝リクエストを表すHash）だけ受け取り、凍結されていない3要素の配列を返します。3要素は順に、HTTPステータス（100以上の整数）、ヘッダ（Hash）、ボディ（`each` か `call` に応答するオブジェクト）です。

`call` に応答すればよいので、lambda でも Rack アプリケーションになります。

```
app = ->(env) do
  [200, { "content-type" => "text/plain" }, ["Hello Rack\n"]]
end
```

サーバー側は、受け取ったHTTPリクエストを env に詰めて `app.call(env)` を呼び、返ってきた配列をHTTPレスポンスに組み立て直すだけです。フレームワークが何であっても、この入口と出口の形が同じなので、サーバーとフレームワークを自由に組み合わせられます。

### 誕生の経緯とPythonのWSGIとの関係

Rack の最初のリリース 0.1 は2007年3月3日で、作者は Leah Neukirchen（旧記事などでは旧名の Christian Neukirchen と書かれていることがあります）です。当時は Mongrel、WEBrick、FastCGI といったサーバーごとにフレームワーク側が個別のアダプタを書いており、Rack はこの組み合わせの数だけ必要だった接続コードを1つの約束事にまとめました。

README の謝辞には「Rack が土台にしている、よく作り込まれ文書化された成果」として WSGI チームの名前があります。Python の WSGI（同期）や ASGI（非同期）が Rack と同じ役割のインターフェースで、Python で FastAPI を ASGI サーバーの [Uvicorn](/tech/details/4843/) で動かす構図は、Ruby で Rails を Rack サーバーの Puma で動かす構図に対応します。

## env（リクエスト環境）に入っている値

env は文字列キーのHashで、CGI 由来のキーと `rack.` で始まる Rack 固有のキーが入ります。Rack 3.2 からはキーが文字列であることが仕様上の必須になりました。主なキーは次のとおりです。

| キー               | 内容                 | 例                 |
| ---------------- | ------------------ | ----------------- |
| REQUEST\_METHOD  | HTTPメソッド（必須）       | GET               |
| PATH\_INFO       | アプリ内でのパス           | /users            |
| QUERY\_STRING    | ?以降（空でも必須）         | id=1              |
| SCRIPT\_NAME     | アプリがマウントされた位置      | /api              |
| SERVER\_PROTOCOL | HTTPバージョン（3.0で必須化） | HTTP/1.1          |
| HTTP\_\*         | リクエストヘッダ           | HTTP\_USER\_AGENT |
| rack.url\_scheme | http・https・ws・wss  | https             |
| rack.input       | リクエストボディ（3.1で任意化）  | IO風オブジェクト         |
| rack.errors      | エラー出力先             | $stderr相当         |

env を直接読むのは手間なので、実際には `Rack::Request.new(env)` で包んで `req.path_info` や `req.params` を使います。`map "/api"` の下にマウントしたアプリへ `/api/users?id=1` を送ると、`SCRIPT_NAME` が `/api`、`PATH_INFO` が `/users` に分かれて届きます（次章のコードで確認できます）。

`rack.input` は Rack 3.1 で「あれば入力ストリーム」という任意項目に変わりました。ボディの無い GET だけを受けるサーバーは渡さなくてもよいため、自作ミドルウェアで `env["rack.input"].read` と書くと nil で落ちることがあります。`Rack::Request#body` も入力ストリームがなければ nil を返すため、取得方法にかかわらず nil を確認してから読みます。

## config.ruとRack::Builder：use・map・runの役割

Rack アプリケーションの構成は、慣例として `config.ru` というファイルに書きます。中身は `Rack::Builder` の記法（Rack DSL）で、`use` がミドルウェアの追加、`map` がパスごとの振り分け、`run` が最後に呼ぶアプリケーションの指定です。

```
# config.ru
require "json"

class Timing
  def initialize(app)
    @app = app
  end

  def call(env)
    started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    status, headers, body = @app.call(env)
    elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started
    headers["x-runtime-ms"] = format("%.2f", elapsed * 1000)
    [status, headers, body]
  end
end

use Rack::Static, urls: ["/public"]
use Rack::Deflater
use Timing

map "/api" do
  run ->(env) {
    req = Rack::Request.new(env)
    [200, { "content-type" => "application/json" }, [JSON.generate(path: req.path_info, q: req.params)]]
  }
end

run ->(env) { [200, { "content-type" => "text/plain" }, ["Hello Rack\n" * 200]] }
```

`use` は書いた順に外側から積まれ、リクエストは上から下へ、レスポンスは下から上へ通ります。上の例では `Rack::Static` が最も外側にあるため、`/public` 配下の静的ファイルは `Rack::Deflater` を通らず、圧縮されずに返ります。圧縮もかけたいなら `Rack::Deflater` を先に書きます。この順序の違いは挙動の差としてそのまま出るので、`use` の並びは意図して決めてください。

Puma 8.0.2 で起動して確かめた結果は次のとおりです。

```
$ gem install rack -v 3.2.7
$ gem install puma -v 8.0.2
$ puma -p 9393 config.ru

$ curl -i "localhost:9393/api/users?id=1"
HTTP/1.1 200 OK
content-type: application/json
x-runtime-ms: 0.37
vary: Accept-Encoding
content-length: 32

{"path":"/users","q":{"id":"1"}}
```

### rackupコマンドとpumaコマンドの使い分け

Rack 3.0（2022-09-06）で、`rackup` コマンド、`Rack::Server`、`Rack::Handler` は rack から別gemの rackup へ切り出されました。rack だけの環境で `rackup` が「command not found」になる、古いコードの `Rack::Handler::WEBrick` が `uninitialized constant Rack::Handler` で落ちる、という症状はこれが原因です。`Rackup::Handler` に読み替えるか、サーバーのコマンドで起動します。

rackup gem の README は、自身を「本番用に設計されていない」と明記しています。現行の rackup 2.3.1 は WEBrick に依存しておらず、メンテナの推奨は「フレームワークは `config.ru` を用意し、利用者は `puma` や `falcon` のコマンドで直接起動する」です。

もう1つの違いは、rackup が環境ごとに自動で挟むミドルウェアです。rackup は既定の development 環境で `Rack::CommonLogger`・`Rack::ShowExceptions`・`Rack::Lint` を積みます。そのため同じ `config.ru` でも、次の結果になりました（rack 3.2.7、rackup 2.3.1、puma 8.0.2 で実行）。

| 起動方法                 | ヘッダ名 Content-Type | ヘッダ名 content-type |
| -------------------- | ----------------- | ----------------- |
| rackup（development）  | 500（LintError）    | 200               |
| rackup -E production | 200               | 200               |
| puma config.ru       | 200               | 200               |

本番相当の起動では大文字ヘッダも通ってしまうため、仕様違反に気づくのは開発時の rackup か、テストで `Rack::Lint` を挟んだときだけです。自作のアプリやミドルウェアは、次章の方法で Lint を通るか確かめておくべきです。

## Rackミドルウェアの書き方とRack 3の制約

ミドルウェアは、次のアプリ（`@app`）を受け取って保持し、自分の `call` の中で `@app.call(env)` を呼ぶクラスです。呼ぶ前に env を書き換えればリクエストの前処理、呼んだ後に3要素を書き換えればレスポンスの後処理になります。`@app.call` を呼ばずに自分でレスポンスを返せば、認証失敗時の401のように処理をその場で打ち切れます。

前章の `Timing` がこの基本形です。計測対象は下流アプリの呼び出しから戻るまでで、ボディの列挙・送信完了までの時間は含みません。ヘッダを足すときは、キーを必ず小文字で書きます。Rack 3 の SPEC は「ヘッダのキーに大文字のASCII文字を含めてはならない」と定めています。

### ボディ加工の制約：eachの直接呼び出し禁止

ボディを書き換えるミドルウェアで避けるべきなのが、受け取ったボディに `each` を呼んで文字列を集める書き方です。Rack 3 の SPEC は「ミドルウェアはボディの `each` を直接呼んではならない。元のボディの `each` を呼ぶ新しいボディを返す」と定めており、`Rack::Lint` を挟むと次のように検出されます。

```
# 仕様違反の例
class BadUpcase
  def initialize(app) = @app = app

  def call(env)
    status, headers, body = @app.call(env)
    text = +""
    body.each { |chunk| text << chunk }
    [status, headers, [text.upcase]]
  end
end
# => Rack::Lint::LintError: Middleware must not call #each directly
```

正しい形は、元のボディを包むボディクラスを返し、`close` を元のボディへ伝えることです。SPEC は、ボディを差し替えた場合も元のボディの `close` を呼ぶよう求めています。長さが変わるので `content-length` も外します。

```
class Upcase
  def initialize(app) = @app = app

  def call(env)
    status, headers, body = @app.call(env)
    headers.delete("content-length")
    [status, headers, UpcaseBody.new(body)]
  end

  class UpcaseBody
    def initialize(body) = @body = body

    def each
      @body.each { |chunk| yield chunk.upcase }
    end

    def close
      @body.close if @body.respond_to?(:close)
    end
  end
end
```

ボディ全体を一度に読みたい場合、SPEC が許しているのは、ボディが `to_ary` に応答するときに限って `to_ary` を呼ぶ方法だけです。なお上の `UpcaseBody` が扱えるのは `each` に応答する Enumerable Body で、`call` 型の Streaming Body（Proc など）を渡すと `undefined method 'each'` で落ちます。Streaming Body を返すアプリの前に置くなら、`respond_to?(:each)` で判定して加工せず返すか、ストリームへの書き込みを変換する別実装が必要です。

### Rack::LintとMockRequestによる仕様適合テスト

サーバーを起動しなくても、`Rack::MockRequest` でリクエストを模擬できます。ミドルウェアの内側と外側の両方に `Rack::Lint` を挟むと、受け取る側・返す側の両方の仕様違反を検出できます。

```
require "rack"
require "rack/mock_request"

inner = ->(env) { [200, { "content-type" => "text/plain", "content-length" => "5" }, ["hello"]] }
app = Rack::Lint.new(Upcase.new(Rack::Lint.new(inner)))

res = Rack::MockRequest.new(app).get("/")
puts res.status  # => 200
puts res.body    # => HELLO
```

Rails や Sinatra のアプリ全体をテストするなら、同じ仕組みの上に作られた rack-test gem（現行 2.2.0）が使えます。Rails の結合テストも内部で rack-test を使っています。

## Rackに同梱されている主なミドルウェア

rack gem 自体にも、よく使うミドルウェアが入っています。rack 3.2.7 の `lib/rack.rb` で autoload されているもののうち、使う機会の多いものを挙げます。

| ミドルウェア                      | 役割                     | 注意点               |
| --------------------------- | ---------------------- | ----------------- |
| Rack::Deflater              | レスポンスのgzip圧縮           | deflate方式には非対応    |
| Rack::Static                | 指定パス配下の静的ファイル配信        | 3.2.6で公開範囲の脆弱性を修正 |
| Rack::Directory             | ディレクトリ一覧の表示            | 本番公開は避ける          |
| Rack::Files                 | ファイル配信（旧Rack::File）    | Rack::Fileは3.1で削除 |
| Rack::ETag / ConditionalGet | ETag付与と304応答           | Railsの既定スタックに入る   |
| Rack::MethodOverride        | \_methodでPUT・DELETEを送る | POSTのフォーム用        |
| Rack::Runtime               | x-runtimeヘッダで処理時間      | Railsの既定スタックに入る   |
| Rack::Lint                  | SPEC違反の検出              | 開発・テスト用           |

`Rack::Deflater` はソースのコメントに「名前に反して deflate エンコーディングには対応しない」とあり、対応するのは gzip と identity だけです。`if:`（条件で圧縮を切り替えるlambda）、`include:`（圧縮対象のContent-Type）、`sync:`（チャンクごとにフラッシュするか、既定 true）の3つのオプションを持ちます。前章の `config.ru` で同じ文を200回繰り返した2,200バイトのレスポンスは、`Accept-Encoding: gzip` 付きで55バイトになりました（繰り返しの多い本文なので、実際のHTMLでここまでは縮みません）。ブラウザとの圧縮方式の交渉は [Accept-Encodingの解説](/tech/details/6855/)で扱っています。nginx や CDN で圧縮しているなら、アプリ側で二重にかける必要はありません。

`Rack::Static` と `Rack::Directory` は、2026-04-01 の rack 3.2.6（3.1.21、2.2系も同日）で脆弱性がまとめて修正されました。`Rack::Directory` のルートディレクトリ漏えい（CVE-2026-34763）と、`Rack::Static` の前方一致で意図しないファイルが見える問題（CVE-2026-34785）です。どちらも公開サーバーで使っているなら、3.2.6 以降へ上げることが先決です。`Rack::Directory` は一覧をそのまま外に見せる機能なので、本番の公開パスには置かないでください。

## RailsとRackの関係：ミドルウェアスタックの確認と追加

Rails アプリケーションは、それ自体が1つの Rack アプリケーションです。新規生成した Rails アプリの `config.ru` には `run Rails.application` が書かれていて、Puma は `Rails.application.call(env)` を呼びます。Rails 8.1 の Gemfile ジェネレータは Puma を既定で入れ、その前段に置く [Thruster](/tech/details/4298/) も既定で追加します。

Rails は `Rack::Builder` の代わりに `ActionDispatch::MiddlewareStack` でミドルウェアを積みます。積まれている中身は次のコマンドで確認できます。

```
$ bin/rails middleware
use ActionDispatch::HostAuthorization
use Rack::Sendfile
use ActionDispatch::Static
use Propshaft::Server
use ActionDispatch::Executor
...
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Rack::TempfileReaper
run MyApp::Application.routes
```

Rails ガイド（Rails on Rack、v8.1.4）が載せている新規アプリの例では28個が積まれ、そのうち `Rack::Sendfile`・`Rack::Runtime`・`Rack::MethodOverride`・`Rack::Head`・`Rack::ConditionalGet`・`Rack::ETag`・`Rack::TempfileReaper` の7個が rack gem 由来です。自作やgemのミドルウェアは `config/application.rb` で追加します。

```
# config/application.rb
config.middleware.use Timing                                  # 最後尾に追加
config.middleware.insert_before Rack::Runtime, Timing          # 指定の前に追加
config.middleware.insert_after ActionDispatch::Executor, Timing # 指定の後に追加
config.middleware.swap ActionDispatch::ShowExceptions, MyShowExceptions
config.middleware.delete Rack::Runtime                         # x-runtimeを出さない
```

`delete` は対象が無くても黙って通り、`delete!` は対象が無ければ例外を出します。[Rails API モード](/tech/details/2905/)ではスタックが小さくなり、Cookie やセッションのミドルウェアは入りません。API モードでセッションを使いたいときに `config.middleware.use ActionDispatch::Cookies` などを自分で足すのはこのためです。

Rails 8.1.4（2026-09-24）の actionpack は `rack >= 2.2.4` を要求しており、Rack 3 は必須ではありません。古いアプリを Rails だけ上げた場合、`Gemfile.lock` の rack が 2.2 系のまま残っていることがあります。`bundle exec ruby -e 'require "rack"; puts Rack.release'` で実際の版を確認できます。

## Rack 2からRack 3で変わった点と移行時に壊れる箇所

Rack 3.0 は2022-09-06、3.1 は2024-06-11、3.2 は2025-07-31 にリリースされました。Rack 2 時代の記事やgemのコードをそのまま使うと、次の箇所で動かなくなるか警告が出ます。

| 変更                          | 版   | 対処                             |
| --------------------------- | --- | ------------------------------ |
| レスポンスヘッダ名の大文字禁止             | 3.0 | content-type 等の小文字で書く          |
| rackup・Rack::Handler を別gemへ | 3.0 | gem rackup を追加、Rackup::Handler |
| Rack::Session を別gemへ        | 3.0 | gem rack-session を追加           |
| ミドルウェアからの body.each 禁止      | 3.0 | ボディを包んで返す                      |
| 複数値ヘッダは配列で渡す                | 3.0 | 改行区切りの文字列をやめる                  |
| rack.input が任意に             | 3.1 | nil を想定して読む                    |
| Rack::File 削除               | 3.1 | Rack::Files に置き換え              |
| :unprocessable\_entity が非推奨 | 3.1 | :unprocessable\_content を使う    |
| Rack::Logger 削除             | 3.2 | 代替なし（ロガーは自前で）                  |
| env のキーは文字列必須               | 3.2 | シンボルキーを入れない                    |

`:unprocessable_entity` は rack 3.2.7 でもまだ422に変換されますが、「将来の版で削除される。`:unprocessable_content` を使うこと」という警告が出ます。Rails のコントローラで `render status: :unprocessable_entity` と書いている箇所が該当し、Rails 8.1 の scaffold はすでに新しい名前で生成します。

### Rack各系列のサポート状況

[rack の README](https://github.com/rack/rack) に載っているサポート方針（2026年9月時点）は次のとおりです。

| 系列      | サポート          | 最新版（リリース日）         |
| ------- | ------------- | ------------------ |
| 3.2.x   | バグ修正とセキュリティ修正 | 3.2.7（2026-08-13）  |
| 3.1.x   | セキュリティ修正のみ    | 3.1.22（2026-08-13） |
| 3.0.x   | サポート終了        | 3.0.18（2025-05-22） |
| 2.2.x   | セキュリティ修正のみ    | 2.2.24（2026-08-13） |
| 2.1.x以前 | サポート終了        | －                  |

3.0 系は、3.1 系や 2.2 系より先に修正が止まっている点に注意が要ります。[CHANGELOG](https://github.com/rack/rack/blob/main/CHANGELOG.md) によると、2026-04-01 には 3.2.6 で CVE 13件、3.1.21 で12件、2.2.23 で9件が修正されました（3.1 系で残った CVE-2026-26962 は 2026-08-13 の 3.1.22 で修正）。3.0 系には修正版が出ておらず、今後も提供されないため、保守中の系列への移行が必要です。rack 3.2 の gemspec が要求する Ruby は 2.4 以上なので、Rack本体の最低要件は満たしやすいものの、併用するサーバーやgemのRuby要件も確認が必要です。Ruby 自体の版とサポート期限は [Rubyの最新バージョン](/tech/details/10592/)で確認できます。

## Rackと組み合わせるWebサーバーの選び方

Rack対応サーバーは共通のインターフェースでアプリを呼び出せますが、対応するRackの版やストリーミング機能、並行処理方式を確認して選びます。rack の README は Puma、Unicorn、Falcon、Pitchfork、Phusion Passenger、Thin、uWSGI、NGINX Unit などを対応サーバーとして挙げています。

- **Puma**（現行 8.0.2）：プロセスとスレッドを併用する。Rails の既定で、迷ったらこれを選ぶ
- **Unicorn**：プロセスのみで動く。スレッドセーフでないコードを抱えたアプリ向けで、遅いクライアントを捌くには前段に nginx などの[リバースプロキシ](/tech/details/13554/)が必須
- **Falcon**：Fiber ベースの非同期サーバー。Rack 3 のストリーミングボディや HTTP/2 を活かす構成で候補になる
- **Pitchfork**：Shopify が Unicorn から派生させたプロセス型サーバー

WEBrick は Ruby 3.0 で標準ライブラリから外れ、[rackup の README](https://github.com/rack/rackup) も WEBrick は「可能なら避けるべき」としています。ローカルの動作確認も `puma config.ru` で足ります。

## Rackを直接書くべき場面とフレームワークに任せる場面

Rack を直接書く価値があるのは、ルーティングが数本で済むヘルスチェックや Webhook の受け口、既存アプリの前に挟む認証・計測・ヘッダ付与、そしてフレームワークに依存させたくない gem の作成です。小さなRackアプリならアプリ側の依存をrack gemだけに絞れる場合がありますが、HTTPで公開するにはPumaなどのサーバーも別途必要です。ミドルウェアとして書いておけば Rails にも Sinatra にも同じコードを差し込めます。

逆に、画面やフォームを持つアプリを Rack だけで組むのは勧めません。CSRF 対策、セッション、パラメータの型変換、エスケープといった、フレームワークが既定で済ませている安全装置を自分で積むことになるからです。ルーティングが増えてきたら、Rack の上に薄く乗る Sinatra（現行 4.2.1、rack 3.0 以上 4 未満を要求）や Roda へ移り、画面が主役なら Rails を選ぶ、という線引きが現実的です。

## よくある質問

### rackupコマンドが「command not found」になるのはなぜですか？

Rack 3.0 から `rackup` コマンドは rack gem に含まれず、別の rackup gem になったためです。`gem install rackup`（Bundler なら `bundle add rackup`）で入ります。ただし本番では `puma config.ru` のようにサーバーのコマンドで直接起動するのが推奨です。gem の追加方法は [gemとBundlerの使い方](/tech/details/4221/)を参照してください。

### Rack::Deflaterはdeflate圧縮にも対応していますか？

対応していません。扱えるのは gzip と identity（無変換）だけで、Brotli や zstd を使いたい場合は nginx や CDN 側で圧縮します。

### 使っているRackのバージョンはどう確認しますか？

アプリのディレクトリで `bundle exec ruby -e 'require "rack"; puts Rack.release'` を実行するか、`Gemfile.lock` の rack の行を見ます。rack 3.2 からは `Rack::VERSION` 定数でも取得できます。

### PythonでRackに相当するものは何ですか？

同期処理の WSGI と、非同期処理の ASGI です。Rack の README は謝辞で WSGI を土台にしたと述べています。Django や Flask が WSGI、FastAPI が ASGI のアプリケーションとして動きます。

### Rails 8.1はRack 2.2でも動きますか？

動きます。Rails 8.1.4 の actionpack が要求するのは `rack >= 2.2.4` です。ただし 2.2 系はセキュリティ修正のみの保守段階なので、Rack 3.2 系へ上げておくのが安全です。

## 関連記事

- [Rubyの最新バージョンは4.0.7｜Ruby 3系との違い・サポート期限・更新手順（2026年9月）](/tech/details/10592/)
- [gemとBundlerの使い方｜Bundler 4系で変わったコマンドとGemfileの書き方](/tech/details/4221/)
- [Thrusterとは？Rails 8のHTTP/2プロキシ設定とKamal 2との役割分担](/tech/details/4298/)
- [Rails APIとは？作成手順・JWT認証・React連携・Docker環境を実装解説【Rails 8.1】](/tech/details/2905/)
- [リバースプロキシとは？仕組み・フォワードプロキシとの違いと導入判断を実装目線で解説](/tech/details/13554/)

---

出典: [Rackとは？Rubyのサーバーとアプリをつなぐ仕様とミドルウェアの書き方【Rack 3.2】](<https://www.issoh.co.jp/tech/details/5598/>)（株式会社一創）
