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実装・運用の判断まで実装者向けに解説で詳しく扱っています。Pusherはこの層を丸ごと外部に預ける選択肢だと捉えると、後述のReverbとの違いが見えやすくなるはずです。
料金プラン:無料Sandboxの上限と有料プランへ上げる分岐点
2026年10月時点の公開プランは以下のとおりです(最新はPusher公式の料金ページで確認してください)。
| プラン | 月額 | 同時接続 | メッセージ/日 |
|---|---|---|---|
| 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公式ドキュメントの「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リポジトリ、ブラウザ側はpusher/pusher-jsのGitHubリポジトリで開発されており、対応する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を使用する理由とその利点について詳しく解説で扱っています)。.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常駐と失敗ジョブ運用までにまとめています。
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・認証・デプロイと選び方を参照)。
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セッションの使い分けが参考になります。
プレゼンスチャンネルで参加中・オンラインのユーザー一覧を表示する
「いまこのルームに誰がいるか」を出したいときは、プライベートチャンネルの拡張であるプレゼンスチャンネルを使います。違いは認可コールバックの戻り値で、真偽値ではなく画面に出してよいユーザー情報の配列を返します。
// 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」節に同じ説明があります)。
動作確認:tinkerとDebug consoleでイベントが届くかを試す
画面を作り込む前に、サーバーからPusherへイベントが出ているかを単体で確かめておくと、後の切り分けが楽になります。手順は3つです。
- Pusherダッシュボードで対象アプリを開き、「Debug console」を表示したままにする。
- 別ターミナルで
php artisan queue:workを起動する(ShouldBroadcastNowなら不要)。 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リファレンスに載っています。
キューワーカーの常駐:Supervisorでqueue:workを止めない設定
開発中はターミナルでqueue:workを動かせば足りますが、本番でプロセスが落ちると、その瞬間からリアルタイム通知が黙って止まります。エラー画面も出ないため気づくのが遅れがちです。Laravel公式の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監視を実装:メトリクス収集・可視化・アラート設定が参考になります。
逆に採用すべきでないのは、Sandboxの無料枠で本番の一般公開サービスを賄おうとする場合です。100接続はページを開いているユーザー数であってアクティブユーザー数ではないため、告知直後のアクセス集中であっさり上限に触れます。上限を超えた接続は、Pusherのプロトコル仕様で定義されたエラーコード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公式ドキュメントの本番運用に関する節を読むと、自前運用で引き受ける作業が具体的にわかります。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リポジトリがアーカイブされ、読み取り専用になりました。新規採用は避け、自前運用を選ぶならReverbにしてください。既存プロジェクトで動いている場合も、Node.jsやSocket.ioの脆弱性修正が来ない以上、移行計画を立てるべき状態です。Socket.IO自体の位置づけやWebSocketとの違いはSocket.IOとは?WebSocketとの違いから実装・スケールまでで整理しています。同じくスターターキット周辺も入れ替えが進んでいるので、古い構成を引きずっているならLaravel Breeze・Jetstreamはなぜ非推奨に?スターターキット(React/Vue/Livewire)への移行と代替案もあわせて確認してください。
WebSocketが要らない場面:SSEやポーリングで足りる要件
PusherもReverbも、双方向で頻繁にやり取りする機能を前提にしたものです。管理画面に「処理が終わりました」と1回出す、ダッシュボードの数値を1分ごとに更新する、といったサーバーからの一方向で低頻度の通知だけなら、Server-Sent Events(SSE)や数十秒間隔のポーリングで十分なことが多く、外部サービスの契約も常駐プロセスも増えません。どちらが向くかの比較はSSEとWebSocketの違いと使い分け|WebRTC・WebTransportまで比較【2026年9月】にまとめています。チャット・共同編集・オンライン表示のように相手の動きを即座に返す機能が入った時点で、Pusherを検討する段階に進むと考えてください。
リアルタイム通知を含む業務システムを新しく作る場合、PusherかReverbかの選択は、認証方式・キュー構成・監視体制と切り離して決められません。要件整理から実装・運用設計までをまとめて相談したいときは、一創のフルスクラッチ開発で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で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実装・運用の判断まで実装者向けに解説
- SSEとWebSocketの違いと使い分け|WebRTC・WebTransportまで比較【2026年9月】
- Laravel Queueの使い方|ジョブ定義からworker常駐と失敗ジョブ運用まで
- Laravel Sanctumとは:SPAとモバイルのトークン認証とCookieセッションの使い分け
- LaravelでViteを使用する理由とその利点について詳しく解説
- LaravelとReactの連携ガイド|環境構築・SPA・認証・デプロイと選び方
- Laravel Breeze・Jetstreamはなぜ非推奨に?スターターキット(React/Vue/Livewire)への移行と代替案
- LaravelでPrometheusとGrafana監視を実装:メトリクス収集・可視化・アラート設定
- Laravel 13で正式安定版になったAI SDKの全体像と導入判断の前提知識