2026年6月1日
2026年6月1日
WordPressにWebSocketでチャット機能を組み込む実装概要とPHPライブラリの選び方
WordPress 本体は「リクエストを受けて応答したら終わる」PHP の仕組みで動いているため、WebSocket の接続を WordPress の中で張り続けることはできません。チャットを作るなら、WebSocket を受け持つ常駐プロセスを別に立て、WordPress は認証とメッセージの保存を担当する、という役割分担が基本になります。この記事では、その全体構成、WordPress 側で用意するもの、PHP で WebSocket サーバーを書く場合のライブラリの選び方、セキュリティと運用の注意点を順に説明します。
なぜ WordPress だけでは WebSocket を扱えないのか
一般的なレンタルサーバーや VPS では、WordPress は PHP-FPM などを通じて「1リクエストごとに PHP を起動し、応答を返して終了する」形で動きます。WebSocket は1本の接続を何分も何時間も開いたままにする通信なので、この実行モデルとは合いません。PHP-FPM のワーカーを接続ごとに占有させると、同時接続数がそのままワーカー数の上限に当たり、通常のページ表示まで詰まります。
そのため、WebSocket を使うには次のどれかが必要です。
- イベント駆動で多数の接続を1プロセスで抱えられる、常駐型の WebSocket サーバー(PHP、Node.js、Go など)
- WebSocket 配信を代行する外部のマネージドサービス
共用レンタルサーバーの多くは、常駐プロセスの起動や任意ポートの待ち受けを認めていません。自前でサーバーを立てる場合は VPS やクラウドのサーバーが前提になる、と考えておくと計画を立てやすくなります。
全体構成:書き込みはWordPress、配信はWebSocket
おすすめは、WordPress と WebSocket サーバーの責任をはっきり分ける構成です。
- ブラウザは WordPress の REST API から、短時間だけ有効な接続用トークンを受け取る
- ブラウザはそのトークンを付けて WebSocket サーバーに接続する(
wss://) - WebSocket サーバーはトークンを検証し、正しければ接続を受け入れる
- メッセージの投稿は WordPress の REST API に送り、権限確認・サニタイズ・保存を WordPress で行う
- 保存に成功したら、WordPress から WebSocket サーバーに「このメッセージを配信して」と内部通知し、WebSocket サーバーが接続中のブラウザに配る
WebSocket サーバー側でメッセージを直接受けて保存する作り方もありますが、その場合はユーザー権限、入力の検証、データベース接続を WordPress の外で二重に実装することになります。書き込みを WordPress に寄せておけば、ユーザー管理・権限・ログの仕組みをそのまま使えます。WordPress から WebSocket サーバーへの内部通知には、ローカルホストだけで受け付ける HTTP エンドポイントや、Redis の Pub/Sub がよく使われます。
WordPress側:接続用トークンを発行する
ブラウザから WebSocket サーバーに WordPress のログイン Cookie をそのまま渡しても、WebSocket サーバー側でそれを検証するのは手間がかかります。そこで、WordPress がログイン中のユーザーにだけ署名付きの短命トークンを発行し、WebSocket サーバーは署名と有効期限を確かめるだけにします。
署名用の秘密鍵は wp-config.php に定数として置き、Git などのリポジトリには含めません。値は推測できない長いランダム文字列にしてください。
// wp-config.php(値はダミー。実際は長いランダム文字列にする)
define( 'MYCHAT_SECRET', 'your-long-random-secret' );
トークン発行用の REST ルートは、独自プラグインに書きます。
add_action( 'rest_api_init', function () {
register_rest_route( 'mychat/v1', '/token', array(
'methods' => 'POST',
'callback' => 'mychat_issue_token',
'permission_callback' => function () {
return is_user_logged_in() && current_user_can( 'read' );
},
) );
} );
function mychat_issue_token() {
$payload = wp_json_encode( array(
'uid' => get_current_user_id(),
'exp' => time() + 60, // 60秒だけ有効
) );
$body = rtrim( strtr( base64_encode( $payload ), '+/', '-_' ), '=' );
$sig = hash_hmac( 'sha256', $body, MYCHAT_SECRET );
return array( 'token' => $body . '.' . $sig );
}
Cookie 認証で REST API を呼ぶときは、X-WP-Nonce ヘッダーに wp_create_nonce( 'wp_rest' ) の値を付ける必要があります。nonce が無いリクエストは、ログイン中でも未ログイン扱いになり、permission_callback で弾かれます。フロントエンドのスクリプトに nonce と REST の URL を渡すには、wp_localize_script() や wp_add_inline_script() を使います。
トークンの有効期限を短くしているのは、接続のときにだけ使う使い捨ての鍵にするためです。接続後の通信は WebSocket の接続自体がユーザーと結び付いているので、長い有効期限は必要ありません。
WebSocket サーバー側:トークンの検証
WebSocket サーバーでは、接続時のクエリ文字列などで受け取ったトークンを次のように検証します。ライブラリに依存しない素の PHP なので、どのライブラリを選んでも接続時の処理から呼び出せます。
function mychat_verify_token( string $token, string $secret ): ?int {
$parts = explode( '.', $token );
if ( count( $parts ) !== 2 ) {
return null;
}
[ $body, $sig ] = $parts;
$expected = hash_hmac( 'sha256', $body, $secret );
if ( ! hash_equals( $expected, $sig ) ) {
return null; // 署名が一致しない
}
$json = base64_decode( strtr( $body, '-_', '+/' ), true );
$data = $json ? json_decode( $json, true ) : null;
if ( ! is_array( $data ) || empty( $data['uid'] ) || empty( $data['exp'] ) ) {
return null;
}
if ( (int) $data['exp'] < time() ) {
return null; // 期限切れ
}
return (int) $data['uid'];
}
署名の比較に === ではなく hash_equals() を使うのは、比較にかかる時間の差から署名を推測される攻撃を避けるためです。秘密鍵は WebSocket サーバー側でも環境変数などから読み込み、コードに直接書かないようにします。WebSocket サーバーが WordPress と別のマシンで動く場合でも、同じ秘密鍵を安全な方法で共有しておく必要があります。
PHP の WebSocket ライブラリの選び方
PHP で WebSocket サーバーを書く場合の代表的な選択肢は次のとおりです。以下は一般的な位置付けですが、開発状況は変わりやすいため(2026年10月現在)、採用前に GitHub のリリース履歴、Issue への対応状況、対応 PHP バージョンを確認してください。
| ライブラリ | 特徴 | 向いている場面 |
|---|---|---|
| Ratchet | ReactPHP を土台にした古くからの定番。解説記事が多い | 小規模な検証や学習。更新が長く止まる時期があるので保守状況の確認が要る |
| Workerman | 純粋な PHP で書かれたイベント駆動の常駐サーバー。複数プロセスで動かせる | 拡張モジュールを入れずに、ある程度の同時接続を扱いたい場合 |
| Swoole / OpenSwoole | C で書かれた PHP 拡張。コルーチンで高い並行性を出せる | 拡張を入れられる自前サーバーで、性能を重視する場合 |
| Amp(amphp/websocket-server) | PHP 8.1 以降の Fiber を使う非同期フレームワーク | 新しめの PHP で、非同期処理全般を Amp で揃えたい場合 |
選ぶときの観点は、主に次の4つです。
- サーバーに拡張モジュールを入れられるか:Swoole 系は拡張のインストールが前提です。入れられない環境なら、純 PHP の Workerman や Ratchet が候補になります。
- 保守が続いているか:WebSocket サーバーは外部に公開する常駐プロセスなので、脆弱性の修正が出ないライブラリは避けたいところです。
- チームが読める言語か:PHP にこだわらず、Node.js の ws や Socket.IO、Go 製の Centrifugo のような専用サーバーを使う選択もあります。WordPress 側とは HTTP と署名付きトークンでつなぐので、言語が違っても構成は同じです。
- 運用を自分たちで持つか:Pusher Channels や Ably のようなマネージドサービスを使うと、常駐プロセスの監視やスケールを任せられます。料金体系や無料枠は変わるため、各サービスの公式サイトで確認してください。
どのライブラリを選んでも、WordPress のコードを WebSocket サーバーのプロセスに読み込むのは避けるのが無難です。WordPress は1リクエストで終わる前提で書かれているため、常駐プロセスに読み込むとメモリの増加やグローバル変数の汚れが起きやすくなります。
リバースプロキシで wss:// を通す
ブラウザからは HTTPS のページと同じドメインで wss:// 接続できるようにしておくと、証明書や Cookie、CORS の扱いが簡単になります。Nginx の場合は、WebSocket サーバーを 127.0.0.1 で待ち受けさせ、特定のパスだけを転送します。
location /ws/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 600s;
}
Upgrade と Connection のヘッダーを渡さないと、WebSocket へのプロトコル切り替えが成立しません。proxy_read_timeout を短いままにすると、無通信の接続がプロキシに切られます。クライアントとサーバーで定期的に ping を送り合う仕組みも入れておくと、途中の機器による切断に強くなります。設定は例なので、お使いの環境に合わせて調整し、変更前に設定ファイルのバックアップを取ってから nginx -t で構文を確認してください。
セキュリティと個人データの扱い
チャットは利用者が自由に文字を送れる機能なので、次の点を最初から組み込んでおきます。
- 表示時のエスケープ:受け取ったメッセージは
textContentで挿入し、innerHTMLには入れない。保存時はsanitize_textarea_field()などで整える - 送信元の確認:WebSocket の接続時に
Originヘッダーを確認し、自サイト以外からの接続を拒否する - 量の制限:1メッセージの最大サイズ、1接続あたりの送信頻度、1ユーザーあたりの同時接続数に上限を設ける
- ユーザー ID はサーバーで決める:ブラウザから送られた「自分は ID 何番」という値は信用せず、トークンから取り出した ID だけを使う
- ログと保存期間:チャットの内容は個人データになり得ます。保存期間と削除方法を決め、プライバシーポリシーに利用目的と保存について記載してください
運用:常駐プロセスを止めない仕組み
WebSocket サーバーはターミナルから手で起動したままにせず、systemd や Supervisor でサービスとして登録し、異常終了したら自動で再起動するようにします。サーバー再起動後に自動起動するかも確認しておきます。いきなり本番に入れず、ステージング環境で同時接続や切断・再接続の動きを試してから公開するのが安全です。
なお、数十秒の遅れが許される通知程度なら、WebSocket を使わずに WordPress の Heartbeat API や定期的な REST API の呼び出しで足りることも多くあります。常駐プロセスの運用負担に見合うかどうかを、最初に見極めてください。
まとめ
- WordPress(PHP-FPM)は接続を張り続けられないため、WebSocket は別の常駐プロセスかマネージドサービスに任せる
- 認証とメッセージの保存は WordPress、配信は WebSocket サーバーと役割を分けると、権限や入力検証を二重に実装せずに済む
- WordPress は短命の署名付きトークンを発行し、WebSocket サーバーは
hash_equals()で署名と期限を検証する - ライブラリは拡張の可否、保守状況、チームの言語、運用体制で選び、採用前にリリース履歴を確認する
- リバースプロキシの Upgrade ヘッダー、表示時のエスケープ、Origin 確認、送信量の制限、チャットログの扱いを最初から設計に入れる