---
title: "Laravel×Pusherの使い方：Laravel 13の設定手順・動作確認とReverbとの使い分け"
url: "https://www.issoh.co.jp/tech/details/2931/"
published: 2024-07-04
updated: 2026-10-02
categories: ["Laravel"]
publisher: "株式会社一創"
---

# Laravel×Pusherの使い方：Laravel 13の設定手順・動作確認とReverbとの使い分け

Pusher（プッシャー）は、WebSocketサーバーを自前で運用せずにリアルタイム通信を追加できるマネージドサービスです。LaravelのBroadcasting機能が公式にPusherドライバーを備えているため、チャットや通知の即時更新は数十行のコードで動きます。一方でLaravel 11以降は設定方法が大きく変わり、古い記事どおりに書くと接続できません。ここではLaravel 13時点の正しい手順に加え、届いているかを確かめる動作確認、プレゼンスチャンネルやキューワーカーの常駐といった本番で詰まる箇所、無料枠の範囲、そして自前ホストのLaravel Reverbとどちらを選ぶかまでを、実装する順番で整理します。

## まとめ：Laravel 13のPusher導入で押さえる5点

- **導入は `php artisan install:broadcasting --pusher` の一発**。`config/broadcasting.php` はLaravel 11以降デフォルトでは存在せず、このコマンドが生成する。
- **環境変数は `BROADCAST_CONNECTION=pusher`**。旧称の `BROADCAST_DRIVER` は使わない。
- **フロント側のキーは `VITE_PUSHER_APP_KEY`**。Laravel Mix時代の `MIX_PUSHER_APP_KEY` は Vite では読み込まれず、これが「キーがundefinedで接続できない」の最頻原因。
- **ブロードキャストはキュー経由**。`php artisan queue:work` が動いていないとイベントは配信されない。本番ではSupervisorなどで常駐させる。
- **無料のSandboxプランは100同時接続・1日20万メッセージ**。これを超え続けるなら、Startup（月49ドル）に上げるか、自前運用のLaravel Reverbへ切り替えるかの分岐に入る。

## Pusherとは：WebSocketをマネージドで提供するリアルタイム通信サービス

Pusherは2010年にロンドンで生まれたリアルタイム通信基盤で、WebSocket接続の維持・スケール・フェイルオーバーを肩代わりします。サーバーがイベントを発行すると、Pusherが接続中のブラウザへ即座に配信する「パブリッシュ／サブスクライブ」型です。本記事で扱うのはWebSocket製品の**Channels**で、モバイルプッシュ通知の**Beams**は別製品となります。なお2020年12月にMessageBird（現Bird）が買収しており、運営体制が変わっています。長期の採用可否を検討するなら、この体制変更を踏まえて後述のReverbと比較してください。

アプリケーション側はPusherのAPIキーを設定するだけで、WebSocketサーバーの常駐プロセスもRedisも用意しません。ポーリング（一定間隔でのHTTPリクエスト）と違い、サーバー発の変化がそのまま画面に反映されるため、チャット・通知バッジ・進捗バー・共同編集のカーソル表示などに向きます。WebSocketそのものの仕組み（ハンドシェイクやフレーム、プロキシ越えの注意点）は[WebSocketとは？仕組み・ハンドシェイクからNode.js実装・運用の判断まで実装者向けに解説](https://www.issoh.co.jp/tech/details/15815/)で詳しく扱っています。Pusherはこの層を丸ごと外部に預ける選択肢だと捉えると、後述のReverbとの違いが見えやすくなるはずです。

### 料金プラン：無料Sandboxの上限と有料プランへ上げる分岐点

2026年10月時点の公開プランは以下のとおりです（最新は[Pusher公式の料金ページ](https://pusher.com/channels/pricing/)で確認してください）。

| プラン              | 月額      | 同時接続   | メッセージ／日 |
| ---------------- | ------- | ------ | ------- |
| Sandbox（クレカ登録不要） | 無料      | 100    | 20万     |
| Startup          | 49ドル    | 500    | 100万    |
| Pro              | 99ドル    | 2,000  | 400万    |
| Business         | 299ドル   | 5,000  | 1,000万  |
| Premium          | 499ドル   | 10,000 | 2,000万  |
| Growth           | 699ドル   | 15,000 | 4,000万  |
| Plus             | 899ドル   | 20,000 | 6,000万  |
| Growth Plus      | 1,199ドル | 30,000 | 9,000万  |
| Enterprise       | 個別      | 個別     | 個別      |

課金はドル建てのため、円換算額は為替で変動します。判断の目安は「同時接続100」です。社内ツールや管理画面の通知なら無料枠で足りますが、一般ユーザー向けサービスで常時100人がページを開いているなら即座に有料プランになります。メッセージは**publish 1回＋購読者数ぶんの配信＝1＋N**で数えます。50人が購読するチャンネルへ1回送れば51メッセージです。参加者が増えるほど消費が跳ね上がる点に注意してください。

見積もりの手順は単純で、「1日の送信回数×平均購読者数」を出して日上限と比べます。たとえば20人が入るチャットルームが30室あり、1室で1日200件の投稿があるなら、200×30×（1＋20）＝12万6,000メッセージ。Sandboxの20万に収まりますが、通知の既読イベントやタイピング表示まで流すと簡単に倍増します。何をリアルタイムで流し、何を画面遷移時の再取得で済ませるかを設計段階で決めておくと、プランの階段を1段上らずに済むことが多いでしょう。

## Laravel 13でPusherを導入する手順：パッケージ追加から.env設定まで

### install:broadcasting –pusherでSDKと設定ファイルの生成

まずPHP側のSDKを入れ、ブロードキャスト機能を有効化します。Laravel 11以降は`config/broadcasting.php`も`routes/channels.php`も初期状態では存在せず、次のコマンドが生成します（手順は[Laravel 13.xのBroadcasting公式ドキュメント](https://laravel.com/framework/docs/13.x/broadcasting)の「Pusher Channels」節と同じです）。

```
composer require pusher/pusher-php-server
php artisan install:broadcasting --pusher
```

このコマンドはPusherの認証情報を対話で尋ね、`.env`への変数追記、フロント側の`resources/js/echo.js`の生成、`laravel-echo`・`pusher-js`のnpmインストールまで面倒を見ます。手動で入れる場合は`npm install --save-dev laravel-echo pusher-js`です。PHP側SDKの実体は[pusher/pusher-http-phpのGitHubリポジトリ](https://github.com/pusher/pusher-http-php)、ブラウザ側は[pusher/pusher-jsのGitHubリポジトリ](https://github.com/pusher/pusher-js)で開発されており、対応するPHPやブラウザの範囲、変更履歴はそれぞれのREADMEとリリース欄で確かめられます。

### .envの設定：BROADCAST\_CONNECTIONとVITE\_プレフィックス

Pusherダッシュボードで作成したアプリのApp ID・Key・Secret・Clusterを`.env`に書きます。Laravelの雛形は`PUSHER_APP_CLUSTER`が`mt1`（米バージニア）になっているため、東京リージョンでアプリを作ったなら`ap3`への書き換えが必須です。ここが実際のアプリと違うと接続が確立しません。

```
BROADCAST_CONNECTION=pusher
PUSHER_APP_ID="your-app-id"
PUSHER_APP_KEY="your-app-key"
PUSHER_APP_SECRET="your-app-secret"
PUSHER_APP_CLUSTER="ap3"
PUSHER_HOST=
PUSHER_PORT=443
PUSHER_SCHEME="https"
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
```

App SecretはPHP側だけが使います。`VITE_`を付けた変数はビルド成果物に埋め込まれてブラウザから読めるため、**Secretに`VITE_`を付けてはいけません**。`.env`を書き換えたあとに設定キャッシュが残っていると古い値で動き続けるので、`php artisan config:clear`もあわせて実行しておきましょう。

### MIX\_PUSHER\_APP\_KEYが効かない原因とVITE\_への移行

古い記事や既存プロジェクトには`process.env.MIX_PUSHER_APP_KEY`が残っています。Laravel 9系の途中でビルドツールがLaravel MixからViteへ変わったため、この書き方はViteでは値を解決できず`undefined`になり、Pusherへの接続が「キー不正」で失敗します。JS側は`import.meta.env.VITE_PUSHER_APP_KEY`へ、`.env`側は`MIX_`接頭辞を`VITE_`へ置き換えてください（Viteへ移行する理由と手順は[LaravelでViteを使用する理由とその利点について詳しく解説](https://www.issoh.co.jp/tech/details/3251/)で扱っています）。`.env`を書き換えたら`npm run build`をやり直さないと古い値がバンドルに残ります。

## ShouldBroadcastイベントを送信してLaravel Echoで受信するコード

### ShouldBroadcastイベントを実装して配信先チャンネルを決める

ブロードキャストされるのは`ShouldBroadcast`インターフェースを実装したイベントだけです。`broadcastOn()`で配信先チャンネルを、`broadcastAs()`でフロントが受け取るイベント名を決めます。

```
php artisan make:event MessageSent
```

```
<?php
// app/Events/MessageSent.php
namespace App\Events;

use App\Models\Message;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class MessageSent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(public Message $message) {}

    public function broadcastOn(): PrivateChannel
    {
        return new PrivateChannel('chat.' . $this->message->room_id);
    }

    public function broadcastAs(): string
    {
        return 'message.sent';
    }

    // 送るデータを絞る（未指定ならpublicプロパティ全体が送られる）
    public function broadcastWith(): array
    {
        return [
            'id' => $this->message->id,
            'body' => $this->message->body,
            'user' => $this->message->user->only(['id', 'name']),
        ];
    }
}
```

あとはコントローラーで`broadcast(new MessageSent($message));`と発火するだけです。`broadcastWith()`を書かない場合はpublicなプロパティがそのままJSONペイロードとしてクライアントへ届くため、モデルを丸ごと渡すとメールアドレスなど画面に不要な列まで送られます。ペイロードは`broadcastWith()`で明示的に絞るのが安全です。

ここで見落としやすいのが、**`ShouldBroadcast`は常にキュー経由で送信される**点です（同期送信したいときだけ`ShouldBroadcastNow`を使います）。Laravel 11以降の既定は`QUEUE_CONNECTION=database`なので、`php artisan queue:work`を動かしていなければイベントはジョブテーブルに溜まったまま配信されません。「コードは正しいのに届かない」ときは最初にここを疑ってください。キューの仕組みと失敗ジョブの扱いは[Laravel Queueの使い方｜ジョブ定義からworker常駐と失敗ジョブ運用まで](https://www.issoh.co.jp/tech/details/17154/)にまとめています。

### echo.jsでEchoを初期化しlistenでイベントを受け取る

`install:broadcasting`が生成する`resources/js/echo.js`は、Echoの初期化だけを行う次の形です。`encrypted: true`は古いオプションで、現在は`forceTLS: true`を使います。

```
// resources/js/echo.js
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
    broadcaster: 'pusher',
    key: import.meta.env.VITE_PUSHER_APP_KEY,
    cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
    forceTLS: true,
});
```

購読はコンポーネント側で書きます。チャットルームのIDを持っている画面なら次のようになります。

```
const roomId = 12;
window.Echo.private(`chat.${roomId}`)
    .listen('.message.sent', (e) => {
        console.log(e.body, e.user.name);
    });

// 画面を離れるときは購読を解除する
// window.Echo.leave(`chat.${roomId}`);
```

`broadcastAs()`で名前を付けた場合、`listen()`の引数は先頭にドットを付けた`'.message.sent'`になります。ドットを落とすとLaravelは名前空間付きのクラス名（`App\Events\MessageSent`）を探すため、イベントを取りこぼします。SPAで画面遷移のたびに購読を重ねると同じイベントを何度も受け取るので、コンポーネントの破棄時に`leave()`を呼んでください。ReactやVueを使うなら`@laravel/echo-react`・`@laravel/echo-vue`の`configureEcho()`と`useEcho`フックが用意されており、購読解除もフック側が受け持ちます（構成の考え方は[LaravelとReactの連携ガイド｜環境構築・SPA・認証・デプロイと選び方](https://www.issoh.co.jp/tech/details/2906/)を参照）。

### routes/channels.phpでプライベートチャンネルを認可する

`PrivateChannel`を使うと、クライアントは接続時に`/broadcasting/auth`へ認可リクエストを送ります。ここで許可を返すルールを`routes/channels.php`に書きます。

```
// routes/channels.php
Broadcast::channel('chat.{roomId}', function ($user, $roomId) {
    return $user->rooms()->whereKey($roomId)->exists();
});
```

ここでtrueを返さないと403となり、チャンネルの購読が失敗します。ログイン状態でしか動かないため、セッションCookieが送られないクロスドメイン構成（APIを別ドメインに置くSPAなど）では、`withCredentials`やSanctumのステートフルドメイン設定を先に整えておくことが必要です。CookieセッションとAPIトークンのどちらで認証するかの判断は[Laravel Sanctumとは：SPAとモバイルのトークン認証とCookieセッションの使い分け](https://www.issoh.co.jp/tech/details/17139/)が参考になります。

### プレゼンスチャンネルで参加中・オンラインのユーザー一覧を表示する

「いまこのルームに誰がいるか」を出したいときは、プライベートチャンネルの拡張であるプレゼンスチャンネルを使います。違いは認可コールバックの戻り値で、真偽値ではなく**画面に出してよいユーザー情報の配列**を返します。

```
// routes/channels.php
Broadcast::channel('room.{roomId}', function ($user, $roomId) {
    if ($user->rooms()->whereKey($roomId)->exists()) {
        return ['id' => $user->id, 'name' => $user->name];
    }
});
```

```
window.Echo.join(`room.${roomId}`)
    .here((users) => { renderMembers(users); })
    .joining((user) => { addMember(user); })
    .leaving((user) => { removeMember(user); })
    .error((error) => { console.error(error); });
```

`here`は参加直後に現在のメンバー全員を、`joining`・`leaving`は増減のたびに1人ずつ受け取ります。イベント側は`broadcastOn()`で`PresenceChannel`を返せば、同じチャンネルで通常のイベントも配信できます。返した配列はチャンネル内の全員に見えるため、メールアドレスや権限情報を入れないようにしてください。

### toOthersとX-Socket-IDで送信者本人への二重表示を防ぐ

チャットでは、投稿者の画面にはHTTPレスポンスで自分の投稿を即時表示し、同じ内容がWebSocket経由でもう一度届いて二重に並ぶ、という不具合がよく起こります。これは送信側で`toOthers()`を付ければ止まります。

```
broadcast(new MessageSent($message))->toOthers();
```

Laravelはリクエストの`X-Socket-ID`ヘッダーから送信者の接続を特定し、その接続だけを配信対象から外します。グローバルなAxiosインスタンスを使っていればEchoがヘッダーを自動で付けますが、`fetch`や独自のHTTPクライアントで送っている場合は付与されません。その場合は`window.Echo.socketId()`の値を自分でヘッダーに載せる必要があります（[公式ドキュメントの「Only to Others」節](https://laravel.com/framework/docs/13.x/broadcasting)に同じ説明があります）。

## 動作確認：tinkerとDebug consoleでイベントが届くかを試す

画面を作り込む前に、サーバーからPusherへイベントが出ているかを単体で確かめておくと、後の切り分けが楽になります。手順は3つです。

1. Pusherダッシュボードで対象アプリを開き、「Debug console」を表示したままにする。
2. 別ターミナルで`php artisan queue:work`を起動する（`ShouldBroadcastNow`なら不要）。
3. `php artisan tinker`からイベントを発火する。

```
php artisan tinker
>>> broadcast(new App\Events\MessageSent(App\Models\Message::first()));
```

Debug consoleに`private-chat.1`宛ての`message.sent`が表示されれば、サーバー側の設定（認証情報・Cluster・キュー）は正しく動いています。ここに何も出ないならPHP側、出ているのにブラウザで受け取れないならEcho側の問題です。原因の半分をこの一手で切り落とせます。

### pusher-php-serverでLaravelを通さずに直接イベントを送る

Laravelのイベントやキューを疑う前に、Pusherの認証情報そのものが正しいかを確かめたい場面もあります。そのときはSDKを直接呼ぶ小さなスクリプトが便利です。

```
<?php
// check_pusher.php（プロジェクト直下で php check_pusher.php）
require __DIR__ . '/vendor/autoload.php';

$pusher = new Pusher\Pusher(
    getenv('PUSHER_APP_KEY'),
    getenv('PUSHER_APP_SECRET'),
    getenv('PUSHER_APP_ID'),
    ['cluster' => getenv('PUSHER_APP_CLUSTER') ?: 'ap3', 'useTLS' => true]
);

$pusher->trigger('test-channel', 'test-event', ['message' => 'hello']);
echo "sent\n";
```

環境変数を渡して実行し、Debug consoleの`test-channel`に`test-event`が出れば、キー・シークレット・Clusterの組み合わせは正しいと判断できます。ここで例外が出るなら、Laravelのコードではなく認証情報かネットワーク（社内プロキシでの443番の遮断など）を調べてください。サーバーから送るHTTP APIの仕様は[Pusher ChannelsのHTTP APIリファレンス](https://pusher.com/docs/channels/server%5Fapi/http-api/)に載っています。

## キューワーカーの常駐：Supervisorでqueue:workを止めない設定

開発中はターミナルで`queue:work`を動かせば足りますが、本番でプロセスが落ちると、その瞬間からリアルタイム通知が黙って止まります。エラー画面も出ないため気づくのが遅れがちです。Laravel公式の[Queuesドキュメント](https://laravel.com/framework/docs/13.x/queues)はSupervisorによる常駐を案内しており、設定例は次のとおりです。

```
; /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/app/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/app/storage/logs/worker.log
stopwaitsecs=3600
```

```
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start "laravel-worker:*"
```

デプロイのたびに`php artisan queue:restart`を実行しないと、ワーカーは古いコードを読み込んだまま動き続けます。また、通知と重いバッチ処理（CSV取り込みやメール一斉送信）を同じキューに入れると、バッチの後ろに通知が並んで数分遅れることがあります。ブロードキャスト専用のキューを分けたい場合は、イベントに`broadcastQueue()`メソッドを定義してキュー名を返し、そのキューだけを処理するワーカーを別に立てる構成が扱いやすいでしょう。

## PusherかReverbか：外部サービスと自前運用の判断基準

2024年のLaravel 11でファーストパーティのWebSocketサーバー**Laravel Reverb**が加わり、「Pusher一択」ではなくなりました。ReverbはPusherプロトコル互換なので、Echo側は`broadcaster`を`'reverb'`に変えるだけで、イベントや認可のコードはそのまま動きます。

結論を言えば、**常時接続が100を超えず、サーバー運用の手を増やしたくないならPusher**、**接続数が数千規模に伸びる見込みがあり、常駐プロセスの監視・再起動を自分たちで回せるならReverb**です。Pusherの費用は接続数に比例して階段状に上がりますが、Reverbは自前のVPS代しかかからない代わりに、プロセス監視・スケールアウト・TLS終端・再接続時の負荷が運用側の責任になります。月数万円の課金を惜しんで、常駐プロセスの死活監視を新設するほうが高くつくケースは珍しくありません。監視の作り込みが必要になったときは[LaravelでPrometheusとGrafana監視を実装：メトリクス収集・可視化・アラート設定](https://www.issoh.co.jp/tech/details/2932/)が参考になります。

逆に**採用すべきでないのは、Sandboxの無料枠で本番の一般公開サービスを賄おうとする場合**です。100接続はページを開いているユーザー数であってアクティブユーザー数ではないため、告知直後のアクセス集中であっさり上限に触れます。上限を超えた接続は、[Pusherのプロトコル仕様](https://pusher.com/docs/channels/library%5Fauth%5Freference/pusher-websockets-protocol/)で定義されたエラーコード`4004`（接続クォータ超過）で拒否されます。このコードは「同じ条件で再接続しても成功しない」区分に入るため、クライアントの自動再接続では回復しません。無料枠は検証と社内利用までと割り切るのが安全です。

### Pusher・Reverb・echo-serverの費用と運用負荷を比べる

| 観点        | Pusher     | Laravel Reverb     | laravel-echo-server |
| --------- | ---------- | ------------------ | ------------------- |
| 提供形態      | マネージドSaaS  | 自前ホスト（PHP）         | 自前ホスト（Node.js）      |
| 費用        | 無料〜月額ドル課金  | サーバー代のみ            | サーバー代のみ             |
| 常駐プロセス    | 不要         | reverb:startを常駐    | Node.jsを常駐          |
| 水平スケール    | Pusher側が担う | Redisで複数台に分散       | 自前で構成               |
| Echoの設定変更 | 基準         | broadcasterとwsHost | Socket.io用に別設定      |
| 保守状況      | 商用サービス     | Laravel公式          | 2023年にアーカイブ         |

表のとおり、PusherとReverbの差は「お金で払うか、運用の手間で払うか」に集約されます。コードの互換性は高いので、最初はPusherで立ち上げ、接続数とプラン料金が見えた段階でReverbへ移す、という順序も現実的な選択です。

### Reverbを本番で動かす条件：ulimit・Supervisor・Redisでの分散

[Laravel 13.xのReverb公式ドキュメント](https://laravel.com/framework/docs/13.x/reverb)の本番運用に関する節を読むと、自前運用で引き受ける作業が具体的にわかります。WebSocketは1接続ごとにファイルディスクリプタを1つ消費するため、OSの上限（`ulimit -n`）を接続数に合わせて引き上げておくことが必要です。Supervisorで常駐させるなら`supervisord.conf`の`minfds`も同じ水準に上げます。1台で足りなくなったら、`REVERB_SCALING_ENABLED=true`を設定して中央のRedisを用意し、複数のReverbサーバーをロードバランサーの背後に並べる構成になります。

```
# 開いているファイル数の上限を確認
ulimit -n

# 受け渡しされるデータを確認しながら起動（本番では外す）
php artisan reverb:start --debug

# コード変更を反映（接続を閉じてから再起動）
php artisan reverb:restart
```

Reverbは常駐プロセスなので、デプロイ後に`reverb:restart`を呼ばないと新しいコードが反映されません。接続数とメッセージ数はLaravel Pulseの`ReverbConnections`・`ReverbMessages`レコーダーで可視化できます。これらを回せる体制がない段階でReverbを選ぶと、障害時に原因の切り分け先が増えるだけになりかねません。

### PusherからReverbへ切り替える手順と.envの書き換え箇所

移行時に書き換えるのは主に設定です。`composer require laravel/reverb`のあと`php artisan reverb:install`を実行すると、`config/reverb.php`と`REVERB_`系の変数が用意されます（新規なら`php artisan install:broadcasting`でReverbを選んでも同じです）。そのうえで`.env`を次のように切り替えます。

```
BROADCAST_CONNECTION=reverb
REVERB_APP_ID=my-app-id
REVERB_APP_KEY=my-app-key
REVERB_APP_SECRET=my-app-secret
REVERB_HOST="ws.example.com"
REVERB_PORT=443
REVERB_SCHEME=https
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
```

```
// resources/js/echo.js（Reverb版）
window.Echo = new Echo({
    broadcaster: 'reverb',
    key: import.meta.env.VITE_REVERB_APP_KEY,
    wsHost: import.meta.env.VITE_REVERB_HOST,
    wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
    wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});
```

イベントクラス・`routes/channels.php`・`listen()`は触りません。切り替え当日はPusher側のアプリを残しておけば、`BROADCAST_CONNECTION`と`echo.js`を戻して`npm run build`するだけで切り戻せます。TLS終端をNginxなどのリバースプロキシで行う場合は、WebSocketのUpgradeヘッダーを転送する設定を忘れないでください。

### laravel-echo-server：新規採用を外すべき理由と移行先

かつての自前ホスト定番だったSocket.io製の`laravel-echo-server`は、**2023年11月15日に[GitHubリポジトリ](https://github.com/tlaverdure/laravel-echo-server)がアーカイブされ、読み取り専用**になりました。新規採用は避け、自前運用を選ぶならReverbにしてください。既存プロジェクトで動いている場合も、Node.jsやSocket.ioの脆弱性修正が来ない以上、移行計画を立てるべき状態です。Socket.IO自体の位置づけやWebSocketとの違いは[Socket.IOとは？WebSocketとの違いから実装・スケールまで](https://www.issoh.co.jp/tech/details/2990/)で整理しています。同じくスターターキット周辺も入れ替えが進んでいるので、古い構成を引きずっているなら[Laravel Breeze・Jetstreamはなぜ非推奨に？スターターキット（React/Vue/Livewire）への移行と代替案](https://www.issoh.co.jp/tech/details/10351/)もあわせて確認してください。

### WebSocketが要らない場面：SSEやポーリングで足りる要件

PusherもReverbも、双方向で頻繁にやり取りする機能を前提にしたものです。管理画面に「処理が終わりました」と1回出す、ダッシュボードの数値を1分ごとに更新する、といった**サーバーからの一方向で低頻度の通知**だけなら、Server-Sent Events（SSE）や数十秒間隔のポーリングで十分なことが多く、外部サービスの契約も常駐プロセスも増えません。どちらが向くかの比較は[SSEとWebSocketの違いと使い分け｜WebRTC・WebTransportまで比較【2026年9月】](https://www.issoh.co.jp/tech/details/4539/)にまとめています。チャット・共同編集・オンライン表示のように相手の動きを即座に返す機能が入った時点で、Pusherを検討する段階に進むと考えてください。

リアルタイム通知を含む業務システムを新しく作る場合、PusherかReverbかの選択は、認証方式・キュー構成・監視体制と切り離して決められません。要件整理から実装・運用設計までをまとめて相談したいときは、一創の[フルスクラッチ開発](https://www.issoh.co.jp/service/system/fullscratch/)でLaravelを含むPHPの受託開発を承っています。

## Pusherに接続できないときの切り分け：症状別の原因と確認場所

Pusher連携の不具合は、原因の切り分け順を決めておくと早く終わります。

- **ブラウザのコンソールに何も出ない**：Echoが初期化されていない。`app.js`で`echo.js`をimportしているか、`npm run build`後のバンドルが配信されているかを確認する。
- **キーがundefined／接続直後に切断**：`MIX_`接頭辞のまま、または`VITE_`変数を追加した後にビルドし直していない。
- **WebSocketは張れるがイベントが来ない**：Clusterの不一致、`listen()`のドット漏れ、キューワーカー停止のいずれか。Pusherダッシュボードの「Debug console」にイベントが届いているかを見れば、サーバー側とクライアント側のどちらの問題か一目でわかる。
- **同じメッセージが2回表示される**：送信者側で`toOthers()`を付けていない、またはSPAの画面遷移で購読を重ねている。
- **403が返る**：`routes/channels.php`の認可がfalseを返している、または未ログイン。
- **接続がエラーコード4004で切られる**：プランの同時接続上限に達している。Sandboxなら100接続を超えた分がここで落ちる。
- **4100番台で切れて再接続を繰り返す**：プロトコル仕様上はPusher側の過負荷を示す区分で、クライアントは1秒以上待ってから再接続する。頻発するなら稼働状況を確認する。
- **全体的に不安定**：自社コードを疑う前に[status.pusher.com](https://status.pusher.com/)でChannelsのREST API・WebSocket client APIの稼働状況を確認する。

## よくある質問

### Pusherの読み方は？

「プッシャー」です。英語のpush（押す）に由来し、サーバーからクライアントへデータを押し出す動作を表しています。同名の一般語（押す人、コイン落としゲームの押し出し機構）とは無関係で、開発文脈ではPusher社のリアルタイム通信サービスを指します。

### Pusherは無料で使える？

Sandboxプランがクレジットカード登録なしの無料で、100同時接続・1日20万メッセージまで使えます。期間制限はないため、開発・検証・社内ツールならこのまま運用できます。上限を超えると接続が拒否されるので、一般公開サービスでは有料プラン（Startup 月49ドル〜）を前提にしてください。

### BROADCAST\_DRIVERを設定しても動かないのはなぜ？

Laravel 11以降、この環境変数は`BROADCAST_CONNECTION`に改名されました。古い名前は読まれないため、既定値の`null`ドライバーが選ばれ、イベントがどこにも送信されません。`.env`を書き換えたうえで`php artisan config:clear`を実行してください。

### pusher-php-serverとpusher-jsの違いは？

`pusher/pusher-php-server`はComposerで入れるPHP側のSDKで、Laravelがイベントを送信するときに使います。`pusher-js`はnpmで入れるブラウザ側のクライアントで、Laravel EchoがWebSocket接続を張るために内部で利用します。役割が違うため、Pusherを使う構成では両方必要です。Reverbへ移った後も、Echoが内部でPusherプロトコルのクライアントを使うため`pusher-js`は残ります。

### PusherからReverbへ移行するとコードはどれだけ変わる？

ReverbはPusherプロトコル互換のため、イベントクラス・`routes/channels.php`・`listen()`の書き方は変わりません。変更は`.env`の接続設定と、Echo初期化の`broadcaster: 'reverb'`および`wsHost`／`wsPort`指定、そしてサーバーで`php artisan reverb:start`を常駐させる運用の追加です。アプリケーションコードよりインフラ側の作業が主になります。

## 関連記事

- [WebSocketとは？仕組み・ハンドシェイクからNode.js実装・運用の判断まで実装者向けに解説](https://www.issoh.co.jp/tech/details/15815/)
- [SSEとWebSocketの違いと使い分け｜WebRTC・WebTransportまで比較【2026年9月】](https://www.issoh.co.jp/tech/details/4539/)
- [Laravel Queueの使い方｜ジョブ定義からworker常駐と失敗ジョブ運用まで](https://www.issoh.co.jp/tech/details/17154/)
- [Laravel Sanctumとは：SPAとモバイルのトークン認証とCookieセッションの使い分け](https://www.issoh.co.jp/tech/details/17139/)
- [LaravelでViteを使用する理由とその利点について詳しく解説](https://www.issoh.co.jp/tech/details/3251/)
- [LaravelとReactの連携ガイド｜環境構築・SPA・認証・デプロイと選び方](https://www.issoh.co.jp/tech/details/2906/)
- [Laravel Breeze・Jetstreamはなぜ非推奨に？スターターキット（React/Vue/Livewire）への移行と代替案](https://www.issoh.co.jp/tech/details/10351/)
- [LaravelでPrometheusとGrafana監視を実装：メトリクス収集・可視化・アラート設定](https://www.issoh.co.jp/tech/details/2932/)
- [Laravel 13で正式安定版になったAI SDKの全体像と導入判断の前提知識](https://www.issoh.co.jp/tech/details/11482/)

---

出典: [Laravel×Pusherの使い方：Laravel 13の設定手順・動作確認とReverbとの使い分け](<https://www.issoh.co.jp/tech/details/2931/>)（株式会社一創）
