---
title: "Actix Web（actix-web）とは？Rustでの使い方とaxumとの違い【4.18.1で実行検証】"
url: "https://www.issoh.co.jp/column/details/3257/"
published: 2024-08-02
updated: 2026-10-28
categories: ["Webシステム"]
publisher: "株式会社一創"
---

# Actix Web（actix-web）とは？Rustでの使い方とaxumとの違い【4.18.1で実行検証】

Actix Web（クレート名は `actix-web`）は、Rustで書かれたWebフレームワークです。2026年10月28日時点の最新版は**4.18.1（2026年10月23日公開）**で、ビルドにはRust 1.89以降が必要です。本記事では4.18.1を rustc 1.99.1 で実際にビルドし、curlで応答を確かめたコードをもとに、インストールからルーティング、リクエストの受け取り方、状態共有とワーカー数、ミドルウェア、テスト、axumとの違い、2020年の「事件」の経緯までを順に説明します。

## まとめ：Actix Webの要点と選びどころ

- 現行は4.x系です。4.0.0（2022年2月25日）からメジャー版を上げずに機能追加を続けており、2026年10月だけで4.16.0から4.18.1まで4つの版が出ました。4.18.0でMSRV（最低サポートRustバージョン）は1.89になっています。
- 4.16.0から、`rustls-0_23` や `openssl` などのTLS機能を有効にしてもHTTP/2が自動では入りません。既定の機能を切っている構成では、`http2` を明示しないとHTTP/1.1だけになります。
- 名前はアクターモデル用の `actix` クレートに由来しますが、Webアプリを書くのに `actix` は不要です。ハンドラは普通の `async fn` で書きます。
- 既定の挙動には落とし穴があります。パスの型変換に失敗すると400ではなく**404**、JSONボディの上限は**2MiB（2,097,152バイト）**、ワーカー数は公式ガイドの「物理CPU数」ではなく、Rust標準ライブラリが見積もる利用可能な並列度で決まり、検証環境では**論理CPU数**と同じ16でした（いずれも4.18.1で実測）。
- Tokioのエコシステム（tower・tower-http）をそのまま使いたいならaxum、属性マクロで書けて4.xの互換性が4年以上続いている点を重く見るならActix Webが候補です。
- 2020年1月に作者がunsafeの扱いを巡る批判で離脱しましたが、プロジェクトは引き継がれ、2026年も2月から10月までに8つの版が出ています。

## Actix Webの現行版と位置づけ

Actix WebはHTTPサーバー、ルーティング、リクエストの型付き取り出し（エクストラクタ）、ミドルウェアを1つのクレートにまとめたフレームワークです。crates.ioの登録は2017年10月23日、1.0.0は2019年6月5日、現行の4.0.0は2022年2月25日に公開されました。[crates.ioのactix-webのページ](https://crates.io/crates/actix-web)によると、累計ダウンロード数は約8,764万、直近90日で約1,249万です（2026年10月28日時点）。Rust自体の基礎は[Rust入門の記事](/tech/details/15373/)にまとめています。

### 4.13.0以降の版とMSRV

直近の版と、[公式のCHANGES.md](https://github.com/actix/actix-web/blob/main/actix-web/CHANGES.md)に書かれた主な変更です。依存に `actix-web = "4"` と書くと4.x系の範囲から版が選ばれますが、どの版になるかは既存の `Cargo.lock`、使っているRustの版、有効にしたフィーチャで変わります。古いRustで `cargo build` が失敗したら、まずツールチェーンの版を疑ってください。4.18.0以降をビルドするにはRust 1.89以降が必要です。

| 版      | 公開日        | MSRV | 主な変更                                           |
| ------ | ---------- | ---- | ---------------------------------------------- |
| 4.18.1 | 2026-10-23 | 1.89 | テスト用にTestRequest::query追加                      |
| 4.18.0 | 2026-10-23 | 1.89 | #\[query\]マクロ・MSRV引き上げ                         |
| 4.17.0 | 2026-10-15 | 1.88 | Acceptヘッダーの品質値の解析を修正                           |
| 4.16.0 | 2026-10-12 | 1.88 | TLS機能がhttp2を有効にしなくなる                           |
| 4.15.0 | 2026-08-21 | 1.88 | Error::add\_response\_mapper追加・io-uring実験機能を削除 |
| 4.14.0 | 2026-06-21 | 1.88 | wrap後のto呼び出しでpanic・生Cookie取得                   |
| 4.13.0 | 2026-02-18 | 1.88 | MSRVを1.75から引き上げ・HTTP/2アップロード高速化                |

表の公開日はcrates.ioのUTC表記で、日本時間では1日後になる場合があります。4.15.0で `experimental-io-uring` フィーチャは削除されました。このフィーチャを有効にしたまま `actix-web = "4"` と書くと、cargoはエラーを出さずにフィーチャが残る4.14.1を選びます。古い記事の設定を写すときは、この指定が含まれていないか確認してください。

### 4.16.0でTLSとHTTP/2の機能が分離

4.15.0までは、`Cargo.toml` で `rustls-0_23` や `openssl` を有効にすると、内部で `http2` フィーチャも一緒に有効になっていました。4.16.0以降は両者が切り離され、TLSの機能定義から `http2` が外れています（4.15.0と4.18.1の `Cargo.toml` を比較して確認）。

影響を受けるのは、ビルド時間や依存を減らすために既定の機能を切っている構成です。`http2` は既定の機能（`default`）には今も含まれているので、`actix-web = { version = "4", features = ["rustls-0_23"] }` のように書いている場合は変わりません。次のように `default-features = false` にしている場合だけ、`http2` を足さないとTLS接続でもHTTP/1.1しか話さなくなります。

```
[dependencies]
actix-web = { version = "4.18", default-features = false, features = ["macros", "rustls-0_23", "http2"] }
```

ビルドもテストもエラーにならず、ブラウザからの接続がHTTP/1.1に落ちるだけなので気付きにくい変更です。4.15以前から上げた環境では、`curl -sI --http2 https://<ホスト>/` の1行目が `HTTP/2` のままかを確かめてください。

### actixクレートとアクターモデルの関係

旧来の解説には「Actix Webはアクターモデルで動く」と書かれたものが多いのですが、4.x系の `actix-web` はアクター用の `actix` クレートに依存していません。`actix` の最新版は0.13.5（2024年6月9日）で止まっています。WebSocketをアクターで扱うための `actix-web-actors` もREADMEで非推奨とされ、アクターを使わない `actix-ws`（0.4.0、2026年2月24日）への移行が案内されています。非同期処理の土台は `actix-rt`（2.15.0）で、READMEの説明は「Tokioベースのシングルスレッド非同期ランタイム」です。`async`/`.await` とTokioの関係は[Rustのasync/await入門](/tech/details/8681/)を参照してください。

## インストールとHello World

Rust 1.89以降とパッケージマネージャーのcargoが入っていれば、プロジェクトの作成と依存の追加は3コマンドで済みます。`rustc --version` が1.88以下なら、先に `rustup update` を実行してください。cargoの基本は[Cargoの使い方の記事](/tech/details/9572/)を参照してください。

```
cargo new hello-actix
cd hello-actix
cargo add actix-web
```

`cargo add` を実行すると、`Cargo.toml` の `[dependencies]` に `actix-web = "4.18.1"` が追記されます。`src/main.rs` を次のように書き換えます。

```
use actix_web::{get, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello, Actix Web!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(hello))
        .bind(("127.0.0.1", 8080))?
        .run()
        .await
}
```

`cargo run` で起動し、別のターミナルから `curl http://127.0.0.1:8080/` を実行すると `Hello, Actix Web!` が返ります。`#[actix_web::main]` は `main` 関数をactix-rtのランタイム上で動かすためのマクロで、`HttpServer::new` に渡すのはアプリの実体ではなく、アプリを作るクロージャ（ファクトリ）です。この違いは後の状態共有の章で効いてきます。

検証に使った環境（Intel Core i9-9880H、macOS）では、serdeとenv\_loggerを含む依存を初めてビルドする `cargo build` に約2分30秒かかりました。2回目以降は差分ビルドなので数秒です。

## ルーティングの書き方と分割

ルートの登録方法は2通りあります。上の例のように `#[get]` `#[post]` などの属性マクロを関数に付けて `.service()` で登録する方法と、`.route("/users", web::get().to(handler))` のようにメソッドを組み立てる方法です。以降のコードはすべて1つの `main.rs` の抜粋で、`serde` と、ログ出力用の `env_logger` も使います。先に `cargo add serde --features derive` と `cargo add env_logger` を実行してください。Serdeの属性は[Serde入門](/tech/details/5551/)で解説しています。ファイル先頭の `use` は次のとおりです。

```
use std::sync::Mutex;

use actix_web::{
    body::MessageBody,
    dev::{ServiceRequest, ServiceResponse},
    error, get, http::StatusCode, middleware::{from_fn, Logger, Next}, post, web, App, Error,
    HttpResponse, HttpServer, Responder, ResponseError,
};
```

```
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct ListQuery {
    page: Option<u32>,
}

#[derive(Deserialize, Serialize)]
struct NewUser {
    name: String,
}

#[get("/users/{id}")]
async fn get_user(path: web::Path<u32>) -> Result<String, ApiError> {
    let id = path.into_inner();
    if id == 0 {
        return Err(ApiError::NotFound);
    }
    Ok(format!("user {id}"))
}

#[get("/users")]
async fn list_users(q: web::Query<ListQuery>) -> String {
    format!("page {}", q.page.unwrap_or(1))
}

#[post("/users")]
async fn create_user(body: web::Json<NewUser>) -> HttpResponse {
    HttpResponse::Created().json(body.into_inner())
}

fn config(cfg: &mut web::ServiceConfig) {
    cfg.service(get_user).service(list_users).service(create_user);
}
```

`App` 側では `.service(web::scope("/api").configure(config))` と書くと、3つのルートがまとめて `/api` 配下に入ります。ユーザー関連は `users.rs`、注文関連は `orders.rs` のように、モジュールごとに `config` 関数を1つずつ公開する形にすると `main` が膨らみません。

### 4.14.0で追加されたRoute::wrapの順序チェック

ルート単位でミドルウェアを掛けるとき、`web::get().wrap(mw).to(handler)` の順に書くと、4.14.0以降はアプリの初期化時にpanicします。以前の版ではエラーにならず、ミドルウェアだけが黙って外れていました。4.18.1で実行したときのメッセージは次のとおりです。

```
Route middleware was already registered with `.wrap()`. Calling `.to()` now would replace the wrapped service and silently drop middleware. Call `.to()` before `.wrap()` (for example: `web::get().to(handler).wrap(mw)`).
```

正しい順序は `web::get().to(handler).wrap(mw)` です。4.13以前で動いていたコードを4.14以降へ上げたら起動直後に落ちた、という場合はこの順序を確認してください。

### 4.18.0で追加された#\[query\]とHTTPのQUERYメソッド

QUERYは、2026年6月に[RFC 10008](https://www.rfc-editor.org/rfc/rfc10008.html)（Proposed Standard）として公開されたHTTPメソッドです。GETと同じく安全で冪等（同じリクエストを繰り返しても、サーバーへの意図した作用が1回送ったときと変わらない）でありながら、POSTのように検索条件をリクエスト本文に入れられます。条件が長くてURLに収まらない検索APIを、POSTで代用せずに書けるようになります。Actix Webは4.18.0で `#[query]` マクロと `guard::Query()` を追加しました。

```
use actix_web::{query, web, App, HttpServer};
use serde::Deserialize;

#[derive(Deserialize)]
struct Search {
    keyword: String,
}

#[query("/search")]
async fn search(body: web::Json<Search>) -> String {
    format!("search {}", body.keyword)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(search))
        .bind(("127.0.0.1", 8080))?
        .run()
        .await
}
```

前章のアプリに組み込む場合は、`use` に `query` を足し、`App` に `.service(search)` を加えます。起動して `curl -X QUERY -H 'content-type: application/json' -d '{"keyword":"rust"}' http://127.0.0.1:8080/search` で呼ぶと200と `search rust` が返りました。同じパスにGETやPOSTを送ると、405ではなく404です。手前にプロキシやCDNを置いている場合は、QUERYメソッドを通す設定になっているかも確認してください。

## Path・Query・Jsonの受け取りと既定のエラー

ハンドラの引数に `web::Path` `web::Query` `web::Json` などを並べると、Actix Webがリクエストから値を取り出して型に変換します。これをエクストラクタと呼びます。変換に失敗したときはハンドラが呼ばれずに自動でエラー応答が返りますが、そのステータスはエクストラクタごとに違います。上のコードを4.18.1で動かし、curlで確かめた結果が次の表です。

| リクエスト                   | ステータス | 返った本文（抜粋）                                 |
| ----------------------- | ----- | ----------------------------------------- |
| `GET /api/users/abc`    | 404   | `can not parse "abc" to a u32`            |
| `GET /api/users?page=x` | 400   | `Query deserialize error`                 |
| POST（nameが無いJSON）       | 400   | `Json deserialize error: missing field`   |
| POST（Content-Type無し）    | 400   | `Content type error`                      |
| POST（2,100,012バイト）      | 413   | `JSON payload ... is larger than allowed` |

注意したいのは1行目です。パスの型変換の失敗は、既定では400 Bad Requestではなく404 Not Foundになります。APIの利用者に「IDの形式が違う」と伝えたいなら、`web::PathConfig` の `error_handler` で400へ差し替えてください。

### JSONボディの上限2MiBとエラー応答の差し替え

`web::Json` の既定の上限は2,097,152バイト（2MiB）です。ソースの `DEFAULT_LIMIT` がこの値で、2,000,012バイトのJSONは201、2,100,012バイトは413になりました。`web::Bytes` や `String` でボディを受ける場合は別の設定（`PayloadConfig`）が効き、上限は262,144バイト（256KiB）です。上限を変えつつ、エラーの本文も自前の形式にしたいときは `JsonConfig` を `app_data` に登録します。

```
App::new()
    .app_data(web::JsonConfig::default().limit(4096).error_handler(|err, _req| {
        error::InternalError::from_response(err, HttpResponse::BadRequest().body("invalid json")).into()
    }))
```

この設定にすると、`name` が欠けたJSONへの応答は400の `invalid json` に変わります。既定のメッセージには構造体のフィールド名が含まれるため、外部公開のAPIでは差し替えておくほうが無難です。

### ResponseErrorによる独自エラー型の応答変換

ハンドラの戻り値を `Result<T, E>` にし、`E` に `ResponseError` を実装すると、`Err` を返しただけで対応するステータスの応答になります。上の `get_user` は、IDが0のとき404と `user not found` を返します。

```
#[derive(Debug)]
enum ApiError {
    NotFound,
}

impl std::fmt::Display for ApiError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "user not found")
    }
}

impl ResponseError for ApiError {
    fn status_code(&self) -> StatusCode {
        StatusCode::NOT_FOUND
    }
}
```

応答の本文には、`fmt` で書いた文字列がそのまま入ります。JSONで返したい場合は `error_response` も実装して `HttpResponse` を組み立ててください。

## web::Dataによる状態共有とワーカー数（本番運用のベストプラクティス）

`HttpServer` は起動時に複数のワーカースレッドを立て、ワーカーごとに `HttpServer::new` のクロージャを呼んでアプリを1つずつ作ります。[公式ガイドのApplicationの章](https://actix.rs/docs/application)は、クロージャの**内側**で作った状態はワーカーごとに別物になり、全体で共有するには**外側**で作ってクロージャへ移す必要がある、と書いています。DBのコネクションプールやカウンターは外側で作ります。

```
struct AppState {
    counter: Mutex<u64>,
}

#[get("/count")]
async fn count(data: web::Data<AppState>) -> String {
    let mut n = data.counter.lock().unwrap();
    *n += 1;
    format!("count {n}")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
    let state = web::Data::new(AppState { counter: Mutex::new(0) });

    HttpServer::new(move || {
        App::new()
            .app_data(state.clone())
            .wrap(Logger::default())
            .wrap(from_fn(add_header))
            .service(hello)
            .service(count)
            .service(web::scope("/api").configure(config))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}
```

`web::Data` の中身は `Arc` なので、`clone()` は参照カウントを増やすだけです。`/count` を2回呼ぶと `count 1`、`count 2` と増えました。`Mutex` は `std::sync::Mutex` を使っています。SQLxのプールを渡す場合も同じ形で、マイグレーションの手順は[SQLx CLIの記事](/tech/details/2901/)にあります。

### ワーカー数の既定値：公式ガイドと実装のずれ

[公式ガイドのServerの章](https://actix.rs/docs/server)には、2026年10月時点でも「ワーカー数の既定値はシステムの物理CPU数」とあります。しかし依存先の `actix-server` 2.10.0のソースは `std::thread::available_parallelism()` で決めています。これはプログラムが利用できる並列度の推定値で、CPUの制限が無いマシンでは論理CPU数に一致するのが普通です。物理8コア・16スレッドの環境で4.18.1を起動したところ、ログは `starting 16 workers` でした。

ワーカーごとにDB接続を張る設計だと、この差で接続数が2倍になります。既定値の上限は512です（actix-server 2.9.4で追加）。Linuxではcgroupのクォータも反映されますが、読み取れない環境では多めに数えることがあるため、接続数を固定したい場合は `.workers(4)` のように明示してください。actix-server 2.9.8からはワーカーを並列に起動するため、ワーカーの多いサーバーほど待ち受け開始が早くなりました。その代わり、ファクトリのクロージャが1つずつ順番に呼ばれる前提で書いた初期化処理は、同期を足す必要があります。キープアライブの既定は5秒で、`.keep_alive()` で変えられます。

### ワーカーを止めるブロッキング処理

1つのワーカーは自分に来たリクエストを1スレッドで処理します。ハンドラの中で `std::thread::sleep` や同期I/Oを呼ぶと、そのワーカーは終わるまで他のリクエストを処理できません。公式ガイドもこの書き方を「Bad practice」として挙げています。待ち時間は `tokio::time::sleep(...).await` のような非同期版に置き換え、重い同期処理は `web::block` で専用のスレッドプールへ逃がします。

### リリースビルドとコンテナでの公開

本番では `cargo build --release` で最適化したバイナリを作ります。上のHello Worldを4.18.1でリリースビルドした実行ファイルは5,205,888バイト（約5.0MiB）で、`strip` でデバッグ用のシンボルを落とすと3,810,120バイト（約3.6MiB）になりました（macOS・x86\_64）。依存クレートは実行ファイルに取り込まれるため、コンテナに入れるのは基本的にこのファイル1つです。ただしmacOSで作った実行ファイルはLinuxのコンテナでは動かないので、実行先のOSとCPUに合わせてビルドし直します。Linuxの既定のターゲットはglibcへ動的リンクするため、実行するイメージにもビルド時と互換のglibcが必要です。`x86_64-unknown-linux-musl` などのmuslターゲットで静的リンクすれば、この依存を外せます。

コンテナで動かすときに多い失敗が、待ち受けアドレスです。`.bind(("127.0.0.1", 8080))` のままだとコンテナ内のループバックでしか待ち受けないため、`docker run -p 8080:8080` でポートを公開しても外から届きません。`.bind(("0.0.0.0", 8080))` にするか、環境変数からアドレスを読む形にしてください。ワーカー数もコンテナに割り当てたCPU数に合わせて `.workers()` で固定しておくと、前節のずれに振り回されません。

## ミドルウェア：Loggerとfrom\_fn

上の `main` ではミドルウェアを2つ掛けています。`Logger` はアクセスログを出す組み込みミドルウェアで、`env_logger` と組み合わせると次の形式で1リクエスト1行が出ます。

```
[2026-10-27T17:01:55Z INFO  actix_web::middleware::logger] 127.0.0.1 "GET /api/users/42 HTTP/1.1" 200 7 "-" "curl/8.7.1" 0.000347
```

独自の処理を挟むなら、4.9.0（2024年8月10日）で追加された `middleware::from_fn` が手軽です。それ以前も `App::wrap_fn` でクロージャを渡す簡易ミドルウェアは書けましたが、関数として切り出して `wrap` で使い回すには `Transform` と `Service` の2つのトレイトを実装する必要があり、数十行の定型コードになっていました。次の関数は、後続の処理が正常に返した応答に `x-app: demo` ヘッダーを付けます。

```
async fn add_header(
    req: ServiceRequest,
    next: Next<impl MessageBody>,
) -> Result<ServiceResponse<impl MessageBody>, Error> {
    let mut res = next.call(req).await?;
    res.headers_mut().insert(
        actix_web::http::header::HeaderName::from_static("x-app"),
        actix_web::http::header::HeaderValue::from_static("demo"),
    );
    Ok(res)
}
```

`next.call(req).await` より前に書いた処理はハンドラの前に、後に書いた処理はハンドラの後に走ります。後続が `Err` を返した場合は `?` でそのまま返るため、ヘッダーは付きません。認証ヘッダーの検査なら `next.call` の前でヘッダーを読み、足りなければ `Err` を返します。

セッションとCORSは本体ではなく `actix-extras` の別クレートです。`actix-session`（0.11.0）は `SessionMiddleware` を `wrap` し、保存先をCookie（`cookie-session` フィーチャ）かRedis（`redis-session` フィーチャ）から選びます。CORSは `actix-cors`（0.7.2）、静的ファイル配信は `actix-files`（0.7.2）を使います。

## actix\_web::testによるハンドラのテスト

サーバーを起動せずにハンドラを呼び出すテストは、本体に含まれる `actix_web::test` で書けます。追加のクレートは要りません。

```
#[cfg(test)]
mod tests {
    use super::*;
    use actix_web::test;

    #[actix_web::test]
    async fn hello_returns_200() {
        let app = test::init_service(App::new().service(hello)).await;
        let req = test::TestRequest::get().uri("/").to_request();
        let resp = test::call_service(&app, req).await;
        assert_eq!(resp.status(), StatusCode::OK);
        let body = test::read_body(resp).await;
        assert_eq!(body, "Hello, Actix Web!");
    }
}
```

4.18.1での `cargo test` の結果は、このテストが `ok` でした。ステータスが200であることと本文の両方を確かめているので、ハンドラが201や空の本文を返すように変わればテストが落ちます。`init_service` には本番と同じ `App` の組み立てを渡せるので、`configure(config)` で分割しておくとテストでも同じルート定義を使い回せます。QUERYメソッドのハンドラを試すときは、4.18.1で追加された `TestRequest::query()` を使います。

## axumとの違いと選び方

RustのWebフレームワークでActix Webと並んで比較されるのが、Tokioの開発チームが作るaxum（2026年10月時点で0.8.9）です。どちらもTokioの上で動くため、選定の分かれ目は性能より設計の土台です。

| 観点     | Actix Web 4.18.1        | axum 0.8.9        |
| ------ | ----------------------- | ----------------- |
| ランタイム  | actix-rt（ワーカーごとに単一スレッド） | Tokio（通常はマルチスレッド） |
| ミドルウェア | 独自のTransform・from\_fn   | towerのServiceを共有  |
| ルート定義  | 属性マクロも使える               | マクロなしでRouterへ登録   |
| 版の安定性  | 2022年から4.x              | 0.x（マイナー版で破壊的変更）  |

tower-httpのタイムアウトや圧縮、gRPCのtonicと同じミドルウェアを使い回したいならaxumを選びます。属性マクロでルートを宣言したい、あるいは版上げのたびにコードを直す手間を減らしたいならActix Webです。ただしワーカーごとに状態が分かれる設計なので、共有したいものは必ずクロージャの外で作る、という前章の規則は守る必要があります。axum側の書き方と両者の詳しい比較表は[Axumの解説記事](/tech/details/7602/)にあります。どちらで組むか決めきれない段階のAPI設計から相談したい場合は、弊社の[API開発・システム連携](/service/system/api/)でも受けています。

### axum以外の選択肢：Rocket・Salvo・ntex・Loco

比較記事で名前が挙がるほかのフレームワークは、2026年10月28日時点で次の版です。直近90日のダウンロード数は、axumが約1億4,325万、Actix Webが約1,249万で、Rocketは約226万でした。axumの数字は他のクレートの依存として入る分を含むので、そのまま利用者数の差とは読めません。

| クレート    | 最新版   | 公開日        | 特徴                    |
| ------- | ----- | ---------- | --------------------- |
| rocket  | 0.5.2 | 2026-10-23 | 属性マクロ中心・0.5.1から約2年半ぶり |
| salvo   | 1.0.1 | 2026-10-04 | 1.0.0は2026-09-24      |
| ntex    | 4.4.1 | 2026-10-27 | Actix Webの原作者が開発      |
| loco-rs | 1.2.0 | 2026-09-24 | axumの上に載るRails風の全部入り  |

業務のAPIサーバーで迷ったら、採用例と周辺クレートの多さでaxumかActix Webの2択から始めるのが現実的です。Rocketは2024年5月の0.5.1から2026年10月まで版が出ていなかった時期があるため、長期保守の案件では更新の頻度を確かめてから選んでください。

## 2020年のunsafe騒動とその後の体制

「actix-web 事件」と呼ばれるのは、2020年1月の作者離脱の件です。当時のActix Webはunsafeコードを多用しており、メモリ安全性を破る箇所が指摘された際、作者のNikolay Kim氏（GitHubではfafhrd91）が修正パッチを採らず、報告のissueを削除したことで批判が集まりました。同氏は2020年1月17日ごろに公開した[「Actix project postmortem」](https://github.com/fafhrd91/actix-web-postmortem)で「I am done with open source.」と書き、リポジトリを個人アカウントへ移しました。

その後リポジトリはactix組織に戻され、開発はコミュニティの保守者に引き継がれました。2026年10月28日時点のGitHubの集計では、コミット数はfafhrd91氏が2,475件で最多、次いでrobjtede氏が1,017件で、作者離脱後も4.x系のリリースは継続しています。現在の版で本番採用を避ける理由にはなりませんが、依存クレートにunsafeが含まれるかを気にする組織なら、`cargo-geiger`（0.13.0）などで自分の依存ツリーを数えてから判断してください。

## 「速い」の根拠とベンチマークの現状

Actix Webの速さの根拠としてよく挙がるのが、TechEmpower Framework Benchmarksです。ただしこのベンチマークは2025年3月17日に公式ブログで発表されたRound 23（結果の確定は同年2月）が最後の回で、[GitHubのリポジトリ](https://github.com/TechEmpower/FrameworkBenchmarks)は2026年3月24日にアーカイブ（読み取り専用）になりました。公開済みの結果はRound 23当時の版で測ったもので、4.18.1や現行のaxumの性能を示すものではありません。

アーカイブ時点のリポジトリには、Rustの実装だけでactix・axum・ntex・xitca-webなど32件が置かれています。ベンチマーク用の実装はDB接続やシリアライズを極限まで詰めたコードで、一般的なAPIサーバーの書き方とは違います。自社のAPIで性能を見積もるなら、実際のハンドラとDBを使って負荷をかけるほうが確実で、Rust製の負荷試験ツールの使い方は[Gooseの記事](/column/details/3099/)で紹介しています。

## よくある質問

### Actix Webの読み方は？

公式の読み方は示されていませんが、日本語では「アクティクス・ウェブ」「アクティックス・ウェブ」と表記されます。公式サイト（actix.rs）の表記は「Actix Web」、crates.ioのクレート名は `actix-web` で、コード中では `actix_web` と書きます。

### Actix Webの最新バージョンはいくつですか？

2026年10月28日時点の最新安定版は4.18.1（2026年10月23日公開）で、ビルドにはRust 1.89以降が必要です。10月だけで4つの版が出ているように更新の間隔が短いため、導入前にcrates.ioのactix-webのページで最新版を確認してください。

### Actix Webを使うのにactixクレートやアクターの知識は必要ですか？

必要ありません。4.x系の `actix-web` はアクター用の `actix` クレートに依存しておらず、ハンドラは `async fn` で書きます。WebSocketもアクター不要の `actix-ws` で書けます（アクター連携用の `actix-web-actors` は非推奨）。

### Actix Webでセッション管理をするには？

`actix-session` クレートを追加し、`SessionMiddleware` を `App` に `wrap` します。保存先はCookieに入れる `CookieSessionStore`（`cookie-session` フィーチャ）か、Redisに置く `RedisSessionStore`（`redis-session` フィーチャ）から選びます。

### パスパラメータの型が合わないと404になるのはなぜですか？

`web::Path` の変換エラーは、既定で404 Not Foundを返す仕様だからです。4.18.1で `Path<u32>` に `abc` を渡すと、404と `can not parse "abc" to a u32` が返りました。400にしたい場合は `web::PathConfig` の `error_handler` で差し替えます。

## 関連記事

- [Axum（アクサム）とは？Rust製Webフレームワークの使い方とActix Web比較【axum 0.8対応】](/tech/details/7602/)
- [Rust入門｜環境構築から所有権・基本文法、受託開発での採用判断まで](/tech/details/15373/)
- [Rustのasync/await入門｜Futureとpollの仕組み、Tokioでの実行とよくあるエラー](/tech/details/8681/)
- [Serdeの読み方は？Rust serde入門と#\[serde\]属性の使い方](/tech/details/5551/)
- [SQLx CLI（sqlx-cli）の使い方｜インストール・マイグレーション・オフラインモードをRustで実践](/tech/details/2901/)

---

出典: [Actix Web（actix-web）とは？Rustでの使い方とaxumとの違い【4.18.1で実行検証】](<https://www.issoh.co.jp/column/details/3257/>)（株式会社一創）
