WebSocket と HTTP/2・HTTP/3 の違いを徹底比較

Web アプリケーションの性能と体験を左右する重要な要素の一つが、通信プロトコルの選択です。リアルタイム性が求められるチャットアプリや動的な株価表示、大量のデータを効率的に転送する API サーバーなど、用途によって最適なプロトコルは大きく異なります。
今回は、モダン Web 開発において特に重要な 3 つのプロトコル「WebSocket」「HTTP/2」「HTTP/3」について、それぞれの特徴や適用場面を詳しく比較し、あなたの開発プロジェクトに最適な選択肢を見つけるお手伝いをします。
背景
WebSocket の登場とリアルタイム通信の需要
従来の HTTP/1.1 では、クライアントからのリクエストに対してサーバーがレスポンスを返すという一方向の通信モデルが基本でした。しかし、チャットアプリケーションやオンラインゲーム、株価のリアルタイム表示など、双方向でリアルタイムな通信が必要なアプリケーションの需要が急激に高まってきました。
このような背景から、2011 年に WebSocket プロトコルが標準化されました。WebSocket は、一度接続を確立すれば、サーバーとクライアント間で自由に双方向通信が可能になる画期的な技術です。
リアルタイム通信の需要が高まる理由を図で示します。
mermaidflowchart TD
A[従来のWebアプリケーション] --> B[ポーリング方式]
B --> C[頻繁なHTTPリクエスト]
C --> D[サーバー負荷増大]
D --> E[レスポンス遅延]
F[リアルタイム通信の需要] --> G[チャットアプリ]
F --> H[オンラインゲーム]
F --> I[株価表示システム]
G --> J[WebSocket導入]
H --> J
I --> J
J --> K[常時接続維持]
K --> L[即座のデータ送受信]
従来のポーリング方式では無駄な通信が発生していましたが、WebSocket により効率的なリアルタイム通信が実現されています。
HTTP/2・HTTP/3 の進化の背景
一方で、従来の Web ページ表示や RESTful API においても、HTTP/1.1 の限界が明らかになってきました。特に、モバイルデバイスの普及や高解像度画像の増加により、より効率的なデータ転送が求められるようになったのです。
HTTP/2 は 2015 年にリリースされ、多重化(マルチプレキシング)やヘッダー圧縮といった機能により、従来の HTTP の性能を大幅に向上させました。さらに、HTTP/3 は 2022 年に標準化され、UDP ベースの QUIC(Quick UDP Internet Connections)プロトコルを採用することで、より高速で信頼性の高い通信を実現しています。
HTTP 進化の流れを時系列で整理します。
mermaidtimeline
title HTTPプロトコルの進化
1991 : HTTP/1.0 : 基本的な要求・応答モデル
1997 : HTTP/1.1 : 持続的接続 : パイプライニング
2015 : HTTP/2 : バイナリプロトコル : 多重化 : ヘッダー圧縮
2022 : HTTP/3 : QUIC採用 : UDP基盤 : 暗号化標準
各世代の HTTP が解決した課題と改善点が明確に分かります。
各プロトコルの基本特性
WebSocket の特徴
WebSocket は、HTTP/1.1 の制約を超えて双方向通信を実現するために設計されたプロトコルです。最初に HTTP でハンドシェイクを行い、その後 WebSocket プロトコルに切り替わる仕組みになっています。
主な特徴
特徴 | 説明 | メリット |
---|---|---|
双方向通信 | クライアント・サーバー双方から任意のタイミングで通信可能 | リアルタイム性の実現 |
永続接続 | 一度確立した接続を維持し続ける | オーバーヘッドの削減 |
低レイテンシ | TCP コネクション上で直接データをやり取り | 即座の応答が可能 |
フレームベース | 軽量なフレーム形式でデータを送受信 | 効率的なデータ転送 |
WebSocket の接続確立から通信までの流れは以下のようになります。
javascript// WebSocket接続の確立
const socket = new WebSocket('ws://localhost:8080');
// 接続が開いた時の処理
socket.addEventListener('open', (event) => {
console.log('WebSocket接続が確立されました');
});
javascript// メッセージの送信
function sendMessage(message) {
socket.send(
JSON.stringify({
type: 'chat',
content: message,
timestamp: Date.now(),
})
);
}
// メッセージの受信
socket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
console.log('受信:', data.content);
});
HTTP/2 の特徴
HTTP/2 は、HTTP/1.1 との互換性を保ちながら、パフォーマンスを大幅に改善したプロトコルです。テキストベースだった HTTP/1.1 に対して、バイナリ形式を採用することで効率化を図りました。
主な特徴
特徴 | 説明 | メリット |
---|---|---|
多重化 | 単一接続で複数のリクエストを並行処理 | ページ読み込み速度の向上 |
ヘッダー圧縮 | HPACK 圧縮によりヘッダーサイズを削減 | 帯域幅の効率的利用 |
サーバープッシュ | サーバーが必要なリソースを先読み送信 | 表示速度の改善 |
バイナリプロトコル | テキストではなくバイナリ形式で通信 | 処理速度の高速化 |
HTTP/2 の多重化機能により、複数のリクエストが効率的に処理される様子を図解します。
mermaidsequenceDiagram
participant C as クライアント
participant S as サーバー
Note over C,S: HTTP/1.1の場合(直列処理)
C->>S: リクエスト1 (CSS)
S-->>C: レスポンス1
C->>S: リクエスト2 (JS)
S-->>C: レスポンス2
C->>S: リクエスト3 (画像)
S-->>C: レスポンス3
Note over C,S: HTTP/2の場合(並列処理)
C->>S: リクエスト1,2,3 (同時送信)
S-->>C: レスポンス1,2,3 (並列処理)
HTTP/2 では複数のリクエストが同時に処理されるため、全体的な読み込み時間が大幅に短縮されます。
HTTP/3 の特徴
HTTP/3 は、TCP の代わりに UDP ベースの QUIC プロトコルを使用する、最新の HTTP バージョンです。Google の SPDY プロトコルの経験を活かし、より高速で安全な通信を実現しています。
主な特徴
特徴 | 説明 | メリット |
---|---|---|
QUIC 採用 | UDP ベースの高速プロトコル使用 | 接続確立の高速化 |
接続移行 | IP アドレス変更時も接続維持 | モバイル環境での安定性 |
標準暗号化 | TLS が標準で組み込まれている | セキュリティの向上 |
ヘッドオブライン阻害解決 | パケットロスの影響を最小化 | 不安定ネットワークでの性能向上 |
HTTP/3 が TCP の課題を解決する仕組みを示します。
mermaidflowchart LR
subgraph "TCP (HTTP/2まで)"
A[順次配送必須] --> B[パケットロス発生]
B --> C[全体通信停止]
C --> D[再送待機]
end
subgraph "QUIC (HTTP/3)"
E[ストリーム独立] --> F[パケットロス発生]
F --> G[該当ストリームのみ影響]
G --> H[他ストリーム継続]
end
style A fill:#ffcccc
style C fill:#ffcccc
style E fill:#ccffcc
style H fill:#ccffcc
HTTP/3 では各ストリームが独立しているため、一部のデータ損失が全体に影響しません。
課題
WebSocket が解決する課題
従来の HTTP ベースの通信では、リアルタイムなアプリケーションを構築する際に以下のような課題がありました。
ポーリングの問題点
javascript// 従来のポーリング方式(非効率的)
function pollServer() {
setInterval(() => {
fetch('/api/messages')
.then((response) => response.json())
.then((data) => {
// 新しいメッセージがあるかチェック
if (data.hasNewMessages) {
updateUI(data.messages);
}
})
.catch((error) => {
console.error('ポーリングエラー:', error);
});
}, 1000); // 1秒ごとにリクエスト
}
この方式では、新しいデータがなくても定期的にリクエストが送信されるため、サーバーリソースの無駄遣いと不要な通信が発生してしまいます。
WebSocket による解決
javascript// WebSocketを使った効率的な実装
const socket = new WebSocket('ws://localhost:8080');
socket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
// 新しいデータが来た時のみ処理
updateUI(data.messages);
});
// サーバーから必要な時のみデータが送られてくる
WebSocket では、サーバー側で変更が発生した際にのみクライアントにデータを送信するため、効率的な通信が実現できます。
HTTP/2・HTTP/3 が解決する課題
HTTP/1.1 の制約
HTTP/1.1 では以下のような問題が顕在化していました。
課題 | 内容 | 影響 |
---|---|---|
ヘッドオブライン阻害 | 1 つのリクエストが遅延すると後続リクエストも待機 | ページ読み込み速度の低下 |
接続数制限 | ブラウザあたり最大 6-8 接続まで | 並列処理の制限 |
ヘッダーの冗長性 | 毎回同じヘッダー情報を送信 | 帯域幅の無駄遣い |
TCP スロースタート | 新規接続時の転送速度制限 | 初回アクセスの遅延 |
HTTP/2・HTTP/3 による改善
javascript// HTTP/1.1時代の並列読み込み対策(非推奨)
function loadResourcesSequentially() {
return fetch('/api/data1')
.then((response) => response.json())
.then((data1) => {
return fetch('/api/data2');
})
.then((response) => response.json())
.then((data2) => {
return fetch('/api/data3');
});
}
javascript// HTTP/2以降では自然に並列処理される
async function loadResourcesParallel() {
const [data1, data2, data3] = await Promise.all([
fetch('/api/data1').then((r) => r.json()),
fetch('/api/data2').then((r) => r.json()),
fetch('/api/data3').then((r) => r.json()),
]);
return { data1, data2, data3 };
}
HTTP/2 以降では、複数のリクエストが自動的に多重化されるため、開発者は並列処理を意識せずとも高速な通信が実現できます。
従来の HTTP/1.1 の限界
パフォーマンス面での制約
従来の HTTP/1.1 では、モダンな Web アプリケーションの要求に対して以下のような限界がありました。
mermaidflowchart TD
A[HTTP/1.1の限界] --> B[単一接続での順次処理]
A --> C[大量のヘッダー送信]
A --> D[サーバープッシュ非対応]
A --> E[暗号化のオーバーヘッド]
B --> F[ページ読み込み遅延]
C --> G[帯域幅の浪費]
D --> H[必要リソースの先読み不可]
E --> I[HTTPS接続の重複コスト]
style F fill:#ffcccc
style G fill:#ffcccc
style H fill:#ffcccc
style I fill:#ffcccc
これらの制約により、特に多くのリソースを必要とする SPA(Single Page Application)や画像の多い Web サイトでは、顕著な性能劣化が見られるようになりました。
解決策
通信方式の違い
3 つのプロトコルの通信方式には根本的な違いがあります。
通信パターンの比較
mermaidstateDiagram-v2
state "WebSocket" as WS {
[*] --> 接続確立
接続確立 --> 双方向通信
双方向通信 --> 双方向通信: メッセージ送受信
双方向通信 --> 接続終了
接続終了 --> [*]
}
state "HTTP/2" as H2 {
[*] --> リクエスト送信
リクエスト送信 --> 並列処理
並列処理 --> レスポンス受信
レスポンス受信 --> [*]
}
state "HTTP/3" as H3 {
[*] --> QUIC接続
QUIC接続 --> ストリーム多重化
ストリーム多重化 --> 高速レスポンス
高速レスポンス --> [*]
}
各プロトコルは異なる通信パターンを持ち、それぞれに適した用途があります。
接続の維持方法
WebSocket の接続管理
javascriptclass WebSocketManager {
constructor(url) {
this.url = url;
this.socket = null;
this.reconnectInterval = 5000;
this.maxReconnectAttempts = 5;
this.reconnectAttempts = 0;
}
connect() {
try {
this.socket = new WebSocket(this.url);
this.setupEventListeners();
} catch (error) {
console.error('WebSocket接続エラー:', error);
this.handleReconnect();
}
}
setupEventListeners() {
this.socket.addEventListener('open', () => {
console.log('WebSocket接続が確立されました');
this.reconnectAttempts = 0;
});
this.socket.addEventListener('close', () => {
console.log('WebSocket接続が閉じられました');
this.handleReconnect();
});
this.socket.addEventListener('error', (error) => {
console.error('WebSocketエラー:', error);
});
}
}
HTTP/2・HTTP/3 の接続管理
javascript// HTTP/2・HTTP/3では自動的に接続プールが管理される
class HttpClient {
constructor() {
// ブラウザが自動的に接続を管理
this.baseURL = 'https://api.example.com';
}
async makeRequest(endpoint) {
try {
// HTTP/2・HTTP/3では自動的に最適化される
const response = await fetch(
`${this.baseURL}${endpoint}`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
);
return await response.json();
} catch (error) {
console.error('HTTP リクエストエラー:', error);
throw error;
}
}
}
WebSocket では開発者が接続の管理を行う必要がありますが、HTTP/2・HTTP/3 ではブラウザが自動的に最適化を行います。
データ転送効率の比較
各プロトコルのデータ転送効率を実際の数値で比較してみましょう。
パフォーマンス比較表
項目 | WebSocket | HTTP/2 | HTTP/3 |
---|---|---|---|
接続確立時間 | 一度のみ(50-100ms) | リクエストごと(20-50ms) | 初回のみ(20-30ms) |
ヘッダーオーバーヘッド | 最小(2-14 バイト) | 圧縮あり(50-200 バイト) | さらに圧縮(30-150 バイト) |
同時接続数 | 1 接続で双方向 | 100 ストリーム/接続 | 制限なし |
再送制御 | TCP | TCP | UDP(独自実装) |
実際のベンチマーク例
javascript// パフォーマンステスト用のコード例
async function benchmarkProtocols() {
const testData = {
messageCount: 1000,
messageSize: '1KB',
};
// WebSocketテスト
const wsStart = performance.now();
await testWebSocket(testData);
const wsTime = performance.now() - wsStart;
// HTTP/2テスト
const h2Start = performance.now();
await testHTTP2(testData);
const h2Time = performance.now() - h2Start;
console.log({
WebSocket: `${wsTime}ms`,
HTTP2: `${h2Time}ms`,
efficiency: `WebSocketが${(
((h2Time - wsTime) / wsTime) *
100
).toFixed(1)}% 高速`,
});
}
リアルタイム通信では WebSocket が圧倒的に効率的ですが、RESTful API では HTTP/2・HTTP/3 が優秀な性能を発揮します。
具体例
WebSocket を使うべきケース
WebSocket は、双方向のリアルタイム通信が必要なアプリケーションで真価を発揮します。
チャットアプリケーション
javascript// チャットアプリのWebSocket実装例
class ChatApplication {
constructor() {
this.socket = new WebSocket('wss://chat.example.com');
this.messageQueue = [];
this.setupEventHandlers();
}
setupEventHandlers() {
this.socket.addEventListener('message', (event) => {
const message = JSON.parse(event.data);
this.displayMessage(message);
});
this.socket.addEventListener('open', () => {
// 接続確立後、溜まっていたメッセージを送信
this.flushMessageQueue();
});
}
sendMessage(text, recipientId) {
const message = {
type: 'chat_message',
content: text,
recipientId: recipientId,
timestamp: Date.now(),
sender: this.userId,
};
if (this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify(message));
} else {
this.messageQueue.push(message);
}
}
}
リアルタイムデータ表示(株価モニター)
javascript// 株価リアルタイム表示システム
class StockPriceMonitor {
constructor() {
this.socket = new WebSocket(
'wss://market.example.com/prices'
);
this.subscribedStocks = new Set();
this.priceData = new Map();
}
subscribeToStock(symbol) {
if (!this.subscribedStocks.has(symbol)) {
this.subscribedStocks.add(symbol);
const subscription = {
type: 'subscribe',
symbol: symbol,
};
this.socket.send(JSON.stringify(subscription));
}
}
handlePriceUpdate(data) {
const { symbol, price, change, volume } = data;
// UIの更新(リアルタイム)
this.updatePriceDisplay(symbol, {
price: price,
change: change,
changePercent: (
(change / (price - change)) *
100
).toFixed(2),
volume: volume,
timestamp: new Date(),
});
// 価格履歴の保存
this.priceData.set(symbol, data);
}
}
オンラインゲーム
javascript// マルチプレイヤーゲームの位置同期
class GameClient {
constructor(gameId, playerId) {
this.gameId = gameId;
this.playerId = playerId;
this.socket = new WebSocket(
`wss://game.example.com/game/${gameId}`
);
this.playerPositions = new Map();
this.setupGameHandlers();
}
sendPlayerMove(x, y, direction) {
const moveData = {
type: 'player_move',
playerId: this.playerId,
position: { x, y },
direction: direction,
timestamp: Date.now(),
};
this.socket.send(JSON.stringify(moveData));
}
handleGameUpdate(data) {
switch (data.type) {
case 'player_move':
this.updatePlayerPosition(
data.playerId,
data.position
);
break;
case 'game_event':
this.handleGameEvent(data.event);
break;
}
}
}
HTTP/2・HTTP/3 を使うべきケース
HTTP/2・HTTP/3 は、従来の Web サイトや REST API で優れた性能を発揮します。
REST API サーバー
javascript// Express.js でのHTTP/2対応API
const express = require('express');
const http2 = require('http2');
const fs = require('fs');
const app = express();
// HTTP/2サーバーの設定
const server = http2.createSecureServer({
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem'),
});
server.on('stream', (stream, headers) => {
// HTTP/2ストリームの処理
if (
headers[':path'] === '/api/users' &&
headers[':method'] === 'GET'
) {
stream.respond({
'content-type': 'application/json',
':status': 200,
});
// 大量のユーザーデータを効率的に送信
const users = getUsersFromDatabase();
stream.end(JSON.stringify(users));
}
});
静的リソースの配信
javascript// HTTP/2サーバープッシュの活用
function handleIndexRequest(stream, headers) {
// HTMLページの送信
stream.respond({
'content-type': 'text/html',
':status': 200,
});
// 必要なリソースを先読みプッシュ
const pushResources = [
{ path: '/css/main.css', type: 'text/css' },
{ path: '/js/app.js', type: 'application/javascript' },
{ path: '/images/logo.png', type: 'image/png' },
];
pushResources.forEach((resource) => {
stream.pushStream(
{
':path': resource.path,
':method': 'GET',
},
(err, pushStream) => {
if (!err) {
pushStream.respond({
'content-type': resource.type,
':status': 200,
});
const content = fs.readFileSync(
`./public${resource.path}`
);
pushStream.end(content);
}
}
);
});
const html = fs.readFileSync('./public/index.html');
stream.end(html);
}
SPA アプリケーション
javascript// Next.js でのHTTP/3対応(設定例)
// next.config.js
module.exports = {
experimental: {
// HTTP/3を有効化
http3: true,
},
// 画像最適化でHTTP/2・HTTP/3の多重化を活用
images: {
domains: ['cdn.example.com'],
formats: ['image/webp', 'image/avif'],
},
// APIルートでの効率的なデータ取得
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://backend.example.com/:path*',
},
];
},
};
実装コード比較
同じ機能を異なるプロトコルで実装した場合の比較を見てみましょう。
リアルタイム通知システムの比較
WebSocket 版(推奨)
javascript// サーバー側(Node.js + ws)
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
class NotificationServer {
constructor() {
this.clients = new Map();
this.setupWebSocketServer();
}
setupWebSocketServer() {
wss.on('connection', (ws, request) => {
const userId = this.extractUserId(request);
this.clients.set(userId, ws);
ws.on('close', () => {
this.clients.delete(userId);
});
});
}
sendNotification(userId, notification) {
const client = this.clients.get(userId);
if (client && client.readyState === WebSocket.OPEN) {
client.send(
JSON.stringify({
type: 'notification',
data: notification,
timestamp: Date.now(),
})
);
}
}
}
HTTP/2 + Server-Sent Events 版(代替案)
javascript// Server-Sent Eventsを使った実装
const express = require('express');
const app = express();
class SSENotificationServer {
constructor() {
this.clients = new Map();
this.setupRoutes();
}
setupRoutes() {
app.get('/notifications/:userId', (req, res) => {
const userId = req.params.userId;
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
'Access-Control-Allow-Origin': '*',
});
this.clients.set(userId, res);
req.on('close', () => {
this.clients.delete(userId);
});
});
}
sendNotification(userId, notification) {
const client = this.clients.get(userId);
if (client) {
client.write(
`data: ${JSON.stringify(notification)}\n\n`
);
}
}
}
WebSocket 版では双方向通信が可能ですが、SSE 版では一方向のみとなります。用途に応じて適切な選択が重要です。
まとめ
WebSocket、HTTP/2、HTTP/3 は、それぞれ異なる課題を解決するために開発された通信プロトコルです。適切な選択をすることで、アプリケーションの性能と使用者体験を大きく向上させることができます。
WebSocket は、リアルタイム性が最重要なアプリケーション(チャット、ゲーム、ライブデータ表示)において最適な選択肢です。双方向通信と低レイテンシが実現でき、サーバーリソースの効率的な利用が可能になります。
HTTP/2 は、従来の Web サイトや REST API の性能を向上させたい場合に適しています。多重化とヘッダー圧縮により、特に多くのリソースを必要とするページで顕著な改善が見込めます。
HTTP/3 は、モバイル環境や不安定なネットワークでの通信品質向上に優れています。QUIC プロトコルの採用により、接続確立の高速化とパケットロス耐性の向上が実現されています。
プロトコル選択の際は、アプリケーションの要求事項(リアルタイム性、データ量、ネットワーク環境)を明確にし、それぞれの特性を理解した上で最適な組み合わせを検討することが重要です。また、一つのアプリケーション内で複数のプロトコルを使い分けることも、現実的で効果的なアプローチと言えるでしょう。
関連リンク
- article
WebSocket と HTTP/2・HTTP/3 の違いを徹底比較
- article
WebSocket の仕組みを図解で理解する
- article
WebRTC と WebSocket の違いをわかりやすく比較
- article
WebSocket 入門:最短 5 分でリアルタイム通信を始めよう
- article
WebSocket とは?HTTP と何が違うのかを徹底解説
- article
htmx で始める WebSocket 連携:ライブ UI の構築術
- article
WordPress のインストール完全手順:レンタルサーバー・Docker・ローカルを徹底比較
- article
gpt-oss で始めるローカル環境 AI 開発入門
- article
GPT-5 で変わる自然言語処理:文章生成・要約・翻訳の精度検証
- article
WebSocket と HTTP/2・HTTP/3 の違いを徹底比較
- article
Emotion で SVG アイコンや画像にスタイルを適用する
- article
WebRTC でビデオチャットアプリを作る手順【初心者向け】
- blog
iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
- blog
Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
- blog
【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
- blog
Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
- blog
Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
- blog
フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来