HTTP/2とHTTP/3の違い:パフォーマンス最適化の実践
Webのパフォーマンスを向上させるため、HTTPプロトコルは継続的に進化してきました。この記事では、HTTP/1.1からHTTP/2、そして最新のHTTP/3までの進化を、実際のパフォーマンス測定と最適化テクニックを通じて理解します。
フロントエンドエンジニアやバックエンドエンジニアが、Webアプリケーションの高速化のために知っておくべき知識を実践的に学びます。
1. HTTPプロトコルの進化
1.1 HTTP/1.1の制限
HTTP/1.1は、以下のような制限がありました:
- 同時接続数の制限: ブラウザごとに最大6-8接続
- ヘッダの重複: リクエストごとにヘッダを送信
- 行頭ブロッキング: 1つのリソースの処理が他のリソースをブロック
- テキストベース: パースにコストがかかる
1.2 HTTP/2の改善点
HTTP/2は、以下の機能を導入しました:
- 多重化(Multiplexing): 1つのTCP接続で複数のリクエスト/レスポンスを並列処理
- ヘッダ圧縮(HPACK): ヘッダの効率的な圧縮
- サーバープッシュ: サーバーがクライアントにリソースを事前に送信
- バイナリプロトコル: テキストからバイナリ形式への変更
1.3 HTTP/3の革新的変更
HTTP/3は、さらに以下の改善を導入しました:
- QUICプロトコル: UDPベースの新しいトランスポート層
- 改良された多重化: ストリームレベルでの多重化
- 接続移行: IPアドレスの変更にも対応
- より高速な接続確立: 0-RTT、1-RTT接続
2. HTTP/2の詳細
2.1 多重化(Multiplexing)
HTTP/2では、1つのTCP接続上で複数のストリームを並列処理できます。
// HTTP/1.1では順次処理
// 画像1のダウンロード完了後、画像2、画像3と順番に処理
// HTTP/2では並列処理
// 画像1、画像2、画像3を同時にダウンロード
2.2 サーバープッシュの実装
// Node.js + ExpressでのHTTP/2サーバープッシュ例
const express = require('express');
const http2 = require('http2');
const fs = require('fs');
const app = express();
const server = http2.createSecureServer({
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt')
}, app);
app.get('/', (req, res) => {
// サーバープッシュの実装
res.push('/styles.css', {
'content-type': 'text/css'
}).end(fs.readFileSync('./styles.css'));
res.push('/app.js', {
'content-type': 'application/javascript'
}).end(fs.readFileSync('./app.js'));
res.writeHead(200);
res.end(`
<html>
<head>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<h1>HTTP/2 Server Push Example</h1>
<script src="/app.js"></script>
</body>
</html>
`);
});
server.listen(443);
2.3 HPACKヘッダ圧縮
HPACKは、HTTP/2のヘッダ圧縮アルゴリズムです。
// ヘッダの効率的な圧縮
// 同じヘッダフィールドは、インデックス参照で圧縮
// 例:
// 1回目: "user-agent: Mozilla/5.0..." (全体を送信)
// 2回目以降: ":user-agent" (インデックスのみ送信)
3. HTTP/3とQUIC
3.1 QUICプロトコル
QUIC(Quick UDP Internet Connections)は、UDP上に構築されたトランスポート層プロトコルです。
QUICの特徴:
- 0-RTT接続: 以前の接続情報を利用して即座に接続
- 改良された多重化: ストリームごとに独立したフロー制御
- 接続移行: ネットワーク変更時の再接続不要
- 組み込みの暗号化: TLSが統合されている
3.2 HTTP/3の実装例
// Node.jsでのHTTP/3実装(node-http3などのライブラリが必要)
// 注意: HTTP/3のサポートはまだ限定的です
// Cloudflare WorkersでのHTTP/3サポート
// HTTP/3は自動的に利用可能(QUICプロトコル)
// クライアント側での確認
fetch('https://example.com', {
method: 'GET',
// HTTP/3は自動的にネゴシエーション
});
4. パフォーマンス測定
4.1 Chrome DevToolsでの確認
// Chrome DevToolsでHTTP/2の使用を確認
// 1. Networkタブを開く
// 2. リクエストをクリック
// 3. HeadersタブでProtocolを確認
// - h2: HTTP/2
// - http/1.1: HTTP/1.1
// - h3: HTTP/3
4.2 パフォーマンス比較スクリプト
// パフォーマンス測定スクリプト
async function measurePerformance(url) {
const results = {
http1: [],
http2: [],
http3: []
};
// HTTP/1.1の測定(複数接続)
for (let i = 0; i < 10; i++) {
const start = performance.now();
await fetch(url, {
// HTTP/1.1を強制(プロキシや設定で制御)
});
results.http1.push(performance.now() - start);
}
// HTTP/2の測定(1接続、多重化)
const start = performance.now();
await Promise.all([
fetch(`${url}/resource1`),
fetch(`${url}/resource2`),
fetch(`${url}/resource3`),
fetch(`${url}/resource4`),
fetch(`${url}/resource5`)
]);
results.http2.push(performance.now() - start);
return {
http1: {
avg: results.http1.reduce((a, b) => a + b) / results.http1.length,
total: results.http1.reduce((a, b) => a + b)
},
http2: {
avg: results.http2[0],
total: results.http2[0]
}
};
}
4.3 WebPageTestでの測定
# WebPageTestを使用した測定
# 1. https://www.webpagetest.org/ にアクセス
# 2. テストURLを入力
# 3. Advanced SettingsでHTTPバージョンを確認
# 4. 結果の"Waterfall View"でHTTP/2の多重化を確認
5. 最適化テクニック
5.1 HTTP/2での最適化
// ❌ HTTP/1.1の最適化(HTTP/2では不要または逆効果)
// - スプライト画像
// - インライン化
// - リソースの結合
// ✅ HTTP/2での最適化
// - リソースの分離(キャッシュ効率を向上)
// - Critical CSSのインライン化(最初のレンダリング)
// - 非クリティカルなリソースの遅延読み込み
// Critical CSSのインライン化例
const html = `
<html>
<head>
<style>
/* Critical CSS */
body { margin: 0; font-family: Arial; }
.header { height: 60px; background: #333; }
</style>
<link rel="preload" href="/styles.css" as="style">
<link rel="stylesheet" href="/styles.css">
</head>
<body>...</body>
</html>
`;
5.2 リソースヒントの活用
<!-- DNSプリフェッチ -->
<link rel="dns-prefetch" href="https://cdn.example.com">
<!-- プリコネクト(DNS + TCP + TLS) -->
<link rel="preconnect" href="https://api.example.com">
<!-- プリロード(重要リソースの早期取得) -->
<link rel="preload" href="/critical.js" as="script">
<link rel="preload" href="/hero-image.jpg" as="image">
<!-- プリフェッチ(次のページのリソース) -->
<link rel="prefetch" href="/next-page.html">
<!-- HTTP/2 Server Pushの代替(プリロード) -->
<link rel="preload" href="/styles.css" as="style">
5.3 リソースの優先度制御
<!-- 高優先度リソース -->
<link rel="stylesheet" href="critical.css">
<script src="critical.js"></script>
<!-- 低優先度リソース -->
<link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">
<script src="analytics.js" defer></script>
<!-- 遅延読み込み -->
<img src="hero.jpg" loading="eager" alt="Hero">
<img src="lazy.jpg" loading="lazy" alt="Lazy">
6. サーバー設定
6.1 NginxでのHTTP/2設定
# /etc/nginx/sites-available/example.com
server {
listen 443 ssl http2; # http2を追加
listen [::]:443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
# HTTP/2 Server Pushの設定
http2_push /styles.css;
http2_push /app.js;
# 静的ファイルの配信
location / {
root /var/www/html;
index index.html;
}
# キャッシュ設定
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# HTTP/1.1からHTTP/2へのリダイレクト
server {
listen 80;
listen [::]:80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
6.2 ApacheでのHTTP/2設定
# /etc/apache2/sites-available/example.com.conf
<VirtualHost *:443>
ServerName example.com
# SSL設定
SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.com.crt
SSLCertificateKeyFile /etc/ssl/private/example.com.key
# HTTP/2を有効化
Protocols h2 http/1.1
# HTTP/2 Server Push
Header add Link "</styles.css>; rel=preload; as=style"
Header add Link "</app.js>; rel=preload; as=script"
DocumentRoot /var/www/html
<Directory /var/www/html>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
6.3 Node.jsでのHTTP/2サーバー
const http2 = require('http2');
const fs = require('fs');
const express = require('express');
const app = express();
const options = {
key: fs.readFileSync('./server.key'),
cert: fs.readFileSync('./server.crt'),
allowHTTP1: true // HTTP/1.1へのフォールバック
};
const server = http2.createSecureServer(options, app);
// 静的ファイルの配信
app.use(express.static('public'));
// HTTP/2 Server Pushのミドルウェア
app.use((req, res, next) => {
if (req.httpVersionMajor === 2) {
// Critical CSSのプッシュ
res.push('/styles/critical.css', {
'content-type': 'text/css'
}).end(fs.readFileSync('./public/styles/critical.css'));
}
next();
});
server.listen(443, () => {
console.log('HTTP/2 server running on port 443');
});
7. パフォーマンス最適化のベストプラクティス
7.1 リソースの最適化
// 画像の最適化
// - WebP形式の使用
// - レスポンシブ画像(srcset)
// - 遅延読み込み
<img
src="image.webp"
srcset="image-400.webp 400w, image-800.webp 800w, image-1200.webp 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
loading="lazy"
alt="Description"
>
// JavaScriptの最適化
// - コード分割
// - ツリーシェイキング
// - ミニファイ
7.2 キャッシュ戦略
// Service Workerでのキャッシュ戦略
self.addEventListener('fetch', (event) => {
if (event.request.destination === 'image') {
event.respondWith(
caches.open('images-v1').then((cache) => {
return cache.match(event.request).then((response) => {
return response || fetch(event.request).then((response) => {
cache.put(event.request, response.clone());
return response;
});
});
})
);
}
});
// HTTPキャッシュヘッダの設定
// Cache-Control: public, max-age=31536000, immutable
8. 実践的なパフォーマンステスト
8.1 Lighthouseでの測定
// Lighthouse CIの設定例
// .lighthouserc.js
module.exports = {
ci: {
collect: {
url: ['https://example.com'],
numberOfRuns: 3
},
assert: {
assertions: {
'categories:performance': ['error', {minScore: 0.9}],
'categories:accessibility': ['error', {minScore: 0.9}],
'categories:best-practices': ['error', {minScore: 0.9}],
'categories:seo': ['error', {minScore: 0.9}]
}
}
}
};
8.2 パフォーマンス監視
// Web Vitalsの測定
import {getCLS, getFID, getFCP, getLCP, getTTFB} from 'web-vitals';
function sendToAnalytics(metric) {
const body = JSON.stringify(metric);
navigator.sendBeacon('/analytics', body);
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);
9. HTTP/3への移行
9.1 サポート状況の確認
// HTTP/3のサポート確認
async function checkHTTP3Support() {
const response = await fetch('https://cloudflare.com', {
method: 'HEAD'
});
// Chrome DevToolsでProtocolを確認
// Networkタブ > Headers > Protocol: h3
}
// ユーザーエージェントでの確認は困難
// サーバー側でALPN(Application-Layer Protocol Negotiation)を確認
9.2 CloudflareなどのCDNの活用
// Cloudflareは自動的にHTTP/3をサポート
// QUICプロトコルが自動的にネゴシエーションされる
// 設定例(Cloudflare Dashboard)
// - Network > HTTP/3 (with QUIC) を有効化
// - 自動的にQUICプロトコルが使用される
10. まとめと次のステップ
この記事を通じて、HTTP/2とHTTP/3の違い、そしてパフォーマンス最適化の実践方法を学びました。
学んだこと
- HTTP/1.1の制限: 同時接続数の制限、行頭ブロッキング
- HTTP/2の改善: 多重化、ヘッダ圧縮、サーバープッシュ
- HTTP/3とQUIC: UDPベース、0-RTT接続、接続移行
- パフォーマンス測定: Chrome DevTools、WebPageTest、Lighthouse
- 最適化テクニック: リソースヒント、優先度制御、キャッシュ戦略
- サーバー設定: Nginx、Apache、Node.jsでのHTTP/2設定
パフォーマンス向上のポイント
- HTTP/2の活用: 多重化による並列処理
- リソースの最適化: 画像、JavaScript、CSSの最適化
- キャッシュ戦略: 適切なキャッシュヘッダとService Worker
- リソースヒント: preload、prefetch、preconnectの活用
- 監視: Web Vitalsによる継続的な監視
次のステップ
- 実サイトでの測定: 自分のサイトでHTTP/2/3の効果を測定
- CDNの活用: Cloudflare、AWS CloudFrontなどのCDN
- Edge Computing: Cloudflare Workers、AWS Lambda@Edge
- 継続的な最適化: パフォーマンス監視と改善のサイクル
Webパフォーマンスは、ユーザー体験に直接影響します。HTTP/2とHTTP/3の理解を深め、実践的な最適化を通じて、高速なWebアプリケーションを構築しましょう!
Happy Optimizing!
コメント
コメントを読み込み中...
