HTTP
HTTP(HyperText Transfer Protocol)完全ガイド
第1章: イントロダクション / HTTPの概要
1.1 HTTPとは何か
HTTP(HyperText Transfer Protocol)は、World Wide Web(WWW)におけるデータ通信の基盤となるアプリケーション層プロトコルである。1989年にTim Berners-Leeによってスイスの欧州原子核研究機構(CERN)で考案され、以来30年以上にわたりインターネット上の情報交換を支え続けている。
HTTPは、クライアント(通常はWebブラウザ)とサーバー間でハイパーテキストドキュメントを転送するために設計されたが、現在ではHTML文書だけでなく、画像、動画、JSON、XML、バイナリデータなど、あらゆる種類のデータの転送に使用されている。RESTful API、マイクロサービス間通信、IoTデバイス間の通信など、現代のインターネットインフラストラクチャの中核をなすプロトコルである。
HTTPの基本的な動作原理は極めてシンプルである。クライアントがサーバーに対してリクエストメッセージを送信し、サーバーがそれに対するレスポンスメッセージを返す。この「リクエスト・レスポンスモデル」がHTTPの根幹であり、そのシンプルさがWebの爆発的な普及を支えた要因の一つである。
クライアント サーバー
| |
| --- HTTP リクエスト ------------> |
| GET /index.html HTTP/1.1 |
| Host: www.example.com |
| |
| <-- HTTP レスポンス ------------ |
| HTTP/1.1 200 OK |
| Content-Type: text/html |
| <html>...</html> |
| |
1.2 Webの基盤としてのHTTP
現代のインターネットは、HTTPなしには成り立たない。以下はHTTPが果たす主要な役割である。
- Webページの配信: ブラウザがHTMLドキュメント、CSS、JavaScriptファイルを取得する
- API通信: モバイルアプリやSPA(Single Page Application)がバックエンドとデータをやり取りする
- マイクロサービス間通信: 分散システムにおけるサービス間のHTTP/REST通信
- ファイル転送: 画像、動画、PDFなどのメディアファイルの配信
- リアルタイム通信: WebSocketのハンドシェイクやServer-Sent Events
- CDN(Content Delivery Network): 世界中のエッジサーバーからのコンテンツ配信
1.3 HTTPの歴史
HTTPの進化は、Webの成長と密接に結びついている。
HTTP/0.9(1991年)
最初のHTTPバージョンは極めて単純なプロトコルであった。GETメソッドのみをサポートし、ヘッダーもステータスコードも存在しなかった。レスポンスはHTMLドキュメントのみであった。
# リクエスト
GET /page.html
# レスポンス(ヘッダーなし、HTMLのみ)
<html>
<body>Hello World</body>
</html>
HTTP/1.0(1996年 — RFC 1945)
HTTP/1.0では多くの重要な機能が追加された。
- HTTPヘッダー: リクエストとレスポンスにメタデータを付与
- ステータスコード: 200, 404, 500などの数値コードによるレスポンス状態の通知
- Content-Type: HTML以外のファイル形式(画像、動画等)のサポート
- POST/HEADメソッド: GET以外のHTTPメソッドの追加
ただし、HTTP/1.0ではリクエストごとにTCP接続を確立・切断する必要があり、パフォーマンスの面で大きな課題があった。
HTTP/1.1(1997年 — RFC 2068、1999年改訂 RFC 2616、2014年改訂 RFC 7230-7235)
HTTP/1.1は最も長く使用されたバージョンであり、以下の重要な改善が含まれている。
- 持続的接続(Persistent Connection / Keep-Alive): デフォルトでTCP接続を維持し、複数リクエストを同一接続で処理
- パイプライニング: レスポンスを待たずに複数リクエストを送信(実装上の問題が多く、実際にはあまり使われなかった)
- チャンク転送エンコーディング: データサイズが不明な場合でもストリーミング転送が可能
- Hostヘッダーの必須化: 一つのIPアドレスで複数のドメインをホスト可能(バーチャルホスティング)
- キャッシュ制御の強化: Cache-Control、ETag、If-None-Match等の導入
- コンテントネゴシエーション: Accept、Accept-Language等によるコンテンツの自動選択
HTTP/2(2015年 — RFC 7540、2022年改訂 RFC 9113)
Googleが開発したSPDYプロトコルを基盤として標準化されたHTTP/2は、パフォーマンスの大幅な改善をもたらした。
- バイナリプロトコル: テキストベースからバイナリフレーミングへの変更
- ストリーム多重化(Multiplexing): 単一のTCP接続上で複数のリクエスト/レスポンスを並行処理
- ヘッダー圧縮(HPACK): ヘッダーサイズの大幅な削減
- サーバープッシュ: クライアントが要求する前にリソースを先行送信
- ストリーム優先度: リソースの読み込み順序を最適化
HTTP/3(2022年 — RFC 9114)
HTTP/3はTCPに代わりQUICプロトコル(UDP上に構築)を採用した革新的なバージョンである。
- QUICベース: UDPベースの信頼性のあるトランスポートプロトコル
- 0-RTT接続確立: 以前接続したサーバーへの即座の再接続
- Head-of-Line Blocking の解消: パケットロス時に他のストリームがブロックされない
- コネクションマイグレーション: ネットワーク切り替え時(Wi-Fi→LTEなど)に接続を維持
1.4 RFCドキュメントとの関係
HTTPの仕様は、IETF(Internet Engineering Task Force)が管理するRFC(Request for Comments)文書として公開されている。主要なRFCは以下の通りである。
| RFC番号 | タイトル | 概要 |
|---|---|---|
| RFC 1945 | HTTP/1.0 | HTTP/1.0の仕様 |
| RFC 2616 | HTTP/1.1 | HTTP/1.1の初期仕様(現在は廃止) |
| RFC 7230-7235 | HTTP/1.1(改訂版) | HTTP/1.1の現行仕様(6分冊) |
| RFC 7540 | HTTP/2 | HTTP/2の初期仕様 |
| RFC 9110 | HTTP Semantics | HTTPの意味論(メソッド、ステータスコード等) |
| RFC 9111 | HTTP Caching | HTTPキャッシュの仕様 |
| RFC 9112 | HTTP/1.1 | HTTP/1.1メッセージ構文の最新仕様 |
| RFC 9113 | HTTP/2 | HTTP/2の最新仕様 |
| RFC 9114 | HTTP/3 | HTTP/3の仕様 |
| RFC 9204 | QPACK | HTTP/3用ヘッダー圧縮 |
2022年に公開されたRFC 9110〜9114は、HTTPの仕様体系を整理し直したものであり、プロトコルのバージョンに依存しない「HTTP Semantics」(RFC 9110)と、各バージョン固有の仕様(RFC 9112〜9114)に分離された。これにより、HTTPの本質的な意味論と、その転送方法を明確に区別できるようになった。
1.5 本記事の構成
本記事では、HTTPプロトコルを以下の観点から包括的に解説する。
- HTTPの基本アーキテクチャとメッセージ構造
- リクエストとレスポンスの詳細
- HTTP/1.1、HTTP/2、HTTP/3の各バージョンの特徴と設定
- セキュリティ(HTTPS/TLS)
- キャッシュ制御
- 認証と認可
- REST APIの設計
- WebSocket、SSE、gRPCとの関係
- 監視、デバッグ、トラブルシューティング
- ロードバランサーとリバースプロキシ
各章では概念の解説に加え、Nginx、Apache、curl等を用いた具体的な設定例やコマンド例を豊富に示し、実務で直接活用できる内容を目指す。
第2章: HTTPの基本アーキテクチャ
2.1 クライアント・サーバーモデル
HTTPは、クライアント・サーバーモデルに基づくプロトコルである。このモデルでは、クライアント(ユーザーエージェント)がリクエストを発行し、サーバーがそれに対するレスポンスを返す。
┌──────────────┐ ┌──────────────┐
│ │ HTTP リクエスト │ │
│ クライアント │ ─────────────────→ │ サーバー │
│ (ブラウザ) │ │ (Webサーバー) │
│ │ HTTP レスポンス │ │
│ │ ←───────────────── │ │
└──────────────┘ └──────────────┘
クライアント(ユーザーエージェント) の例:
- Webブラウザ(Chrome, Firefox, Safari, Edge)
- CLIツール(curl, wget, HTTPie)
- モバイルアプリのHTTPクライアント
- プログラミング言語のHTTPライブラリ(Python requests, Node.js axios)
- 検索エンジンのクローラー(Googlebot)
サーバー の例:
- Webサーバー(Nginx, Apache HTTP Server, Caddy)
- アプリケーションサーバー(Tomcat, Gunicorn, Puma, Node.js)
- APIサーバー(Express.js, Django REST Framework, FastAPI)
2.2 リクエスト/レスポンスサイクル
HTTPのリクエスト/レスポンスサイクルは以下の手順で行われる。
- DNS解決: クライアントがドメイン名をIPアドレスに解決する
- TCP接続確立: 3ウェイハンドシェイクによりTCP接続を確立する(HTTP/3ではQUIC接続)
- TLSハンドシェイク: HTTPS通信の場合、TLSハンドシェイクを実行する
- HTTPリクエスト送信: クライアントがHTTPリクエストメッセージを送信する
- サーバー処理: サーバーがリクエストを処理する
- HTTPレスポンス送信: サーバーがHTTPレスポンスメッセージを返送する
- 接続管理: Keep-Aliveの場合は接続を維持、そうでなければ切断する
クライアント サーバー
| |
| ---- SYN ---------------------------------→ | TCP
| ←--- SYN-ACK ----------------------------- | 3ウェイ
| ---- ACK ---------------------------------→ | ハンドシェイク
| |
| ---- ClientHello -------------------------→ | TLS
| ←--- ServerHello, Certificate ------------- | ハンドシェイク
| ---- ClientKeyExchange, Finished ---------→ | (HTTPSの場合)
| ←--- Finished ---------------------------- |
| |
| ---- GET /index.html HTTP/1.1 -----------→ | HTTP
| ←--- HTTP/1.1 200 OK -------------------- | リクエスト/
| Content-Type: text/html | レスポンス
| <html>...</html> |
| |
2.3 ステートレス性とその意味
HTTPはステートレス(stateless)プロトコルである。これは、各リクエスト/レスポンスの対話が完全に独立しており、サーバーは前のリクエストの情報を保持しないことを意味する。
ステートレスの利点
- スケーラビリティ: サーバーがクライアントの状態を保持しないため、リクエストを任意のサーバーに振り分けられる
- 信頼性: サーバー障害時に他のサーバーが即座に代替できる
- シンプルさ: プロトコルの設計と実装が単純になる
- キャッシュ可能性: 状態に依存しないレスポンスはキャッシュしやすい
ステートレスの課題と解決策
ステートレスであるがゆえに、ユーザーのログイン状態やショッピングカートの内容などを維持するには、追加の仕組みが必要となる。
| 課題 | 解決策 |
|---|---|
| ユーザー認証状態の維持 | Cookie + Session / JWT |
| 一連のトランザクション管理 | サーバー側セッションストア |
| ユーザー設定の保持 | Cookie / LocalStorage |
| API認証 | Bearer Token / API Key |
# Cookie を使ったセッション管理の例
# 1. ログインリクエスト
POST /login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
username=user&password=pass
# 2. サーバーからのレスポンス(Cookieの設定)
HTTP/1.1 200 OK
Set-Cookie: session_id=abc123; Path=/; HttpOnly; Secure; SameSite=Strict
# 3. 後続のリクエスト(Cookieの送信)
GET /dashboard HTTP/1.1
Host: example.com
Cookie: session_id=abc123
2.4 プロキシ、ゲートウェイ、トンネルの役割
HTTP通信において、クライアントとサーバーの間に様々な中間者(intermediary)が介在する場合がある。
フォワードプロキシ
フォワードプロキシは、クライアント側に配置され、クライアントの代理としてサーバーにリクエストを送信する。
クライアント → フォワードプロキシ → インターネット → サーバー
主な用途:
- アクセス制御(社内ネットワークからの外部アクセスの制限)
- キャッシュ(よくアクセスされるコンテンツのローカルキャッシュ)
- 匿名性の確保(クライアントのIPアドレスの隠蔽)
- コンテンツフィルタリング
リバースプロキシ
リバースプロキシは、サーバー側に配置され、クライアントからのリクエストを背後のサーバーに転送する。
クライアント → インターネット → リバースプロキシ → バックエンドサーバー群
主な用途:
- ロードバランシング(複数サーバーへのリクエスト分散)
- SSL終端(暗号化/復号化の集約処理)
- キャッシュ(静的コンテンツのキャッシュ)
- セキュリティ(WAF機能、DDoS保護)
- 圧縮(レスポンスのgzip/brotli圧縮)
# Nginx リバースプロキシ設定例
upstream backend_servers {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
location / {
proxy_pass http://backend_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
ゲートウェイ
ゲートウェイは、異なるプロトコル間の橋渡しを行う中間者である。例えば、HTTPリクエストをFTP、SMTP、またはデータベースプロトコルに変換する。API Gatewayは、マイクロサービスアーキテクチャにおいて、外部からのHTTPリクエストを内部サービスにルーティングする重要な役割を果たす。
トンネル
トンネルは、HTTPのCONNECTメソッドを使用して、2つのエンドポイント間に透過的な通信路を確立する。主にHTTPプロキシを経由したHTTPS通信に使用される。
# CONNECTメソッドによるトンネル確立
CONNECT www.example.com:443 HTTP/1.1
Host: www.example.com:443
# プロキシからの応答
HTTP/1.1 200 Connection Established
# 以降、TLS通信がトンネルを通じて行われる
2.5 URI(Uniform Resource Identifier)の構造と役割
URIは、インターネット上のリソースを一意に識別するための文字列である。HTTPにおいてURIは、アクセスするリソースの場所を指定するために使用される。
URI の構造:
┌─────────────────────────────────────────────────────────────────────┐
│ https://user:pass@www.example.com:8080/path/to/resource?key=val#frag │
│ └─┬──┘ └──┬───┘ └──────┬───────┘└┬─┘└──────┬───────┘└──┬───┘└┬──┘ │
│ scheme userinfo host port path query fragment │
│ └──┬──┘ └─────────────┬──────────────┘ │
│ scheme authority │
└─────────────────────────────────────────────────────────────────────┘
各構成要素の説明:
| 要素 | 説明 | 例 |
|---|---|---|
| scheme | プロトコルの種類 | http, https, ftp |
| userinfo | ユーザー認証情報(非推奨) | user:password |
| host | サーバーのホスト名またはIPアドレス | www.example.com, 192.168.1.1 |
| port | ポート番号(省略時はschemeのデフォルト) | 8080(HTTPは80、HTTPSは443) |
| path | リソースのパス | /api/v1/users |
| query | クエリパラメータ | ?page=1&limit=20 |
| fragment | フラグメント識別子(サーバーには送信されない) | #section-2 |
URLエンコーディング
URIで使用できない文字(日本語、スペース、特殊文字等)は、パーセントエンコーディングで表現する。
# 日本語を含むURL
https://example.com/search?q=HTTP入門
↓ エンコード後
https://example.com/search?q=HTTP%E5%85%A5%E9%96%80
# スペースを含むURL
https://example.com/my file.html
↓ エンコード後
https://example.com/my%20file.html
# Python でのURLエンコーディング
from urllib.parse import quote, unquote
encoded = quote("HTTP入門")
print(encoded) # HTTP%E5%85%A5%E9%96%80
decoded = unquote("HTTP%E5%85%A5%E9%96%80")
print(decoded) # HTTP入門
第3章: HTTPリクエストの詳細
3.1 HTTPリクエストメッセージの構造
HTTPリクエストメッセージは、以下の3つの部分で構成される。
┌─────────────────────────────────────────┐
│ リクエストライン │
│ GET /api/users?page=1 HTTP/1.1 │
├─────────────────────────────────────────┤
│ ヘッダーフィールド │
│ Host: api.example.com │
│ Accept: application/json │
│ Authorization: Bearer eyJhbGc... │
│ User-Agent: Mozilla/5.0... │
│ Accept-Encoding: gzip, deflate, br │
├─────────────────────────────────────────┤
│ 空行(CRLF) │
├─────────────────────────────────────────┤
│ メッセージボディ(オプション) │
│ {"name": "John", "email": "j@ex.com"} │
└─────────────────────────────────────────┘
リクエストラインのフォーマット: メソッド SP リクエストターゲット SP HTTPバージョン CRLF
3.2 HTTPメソッド
HTTPは9つの標準メソッドを定義している。各メソッドには安全性(Safe)、冪等性(Idempotent)、キャッシュ可能性(Cacheable)の属性がある。
| メソッド | 説明 | 安全 | 冪等 | ボディ | キャッシュ |
|---|---|---|---|---|---|
| GET | リソースの取得 | ○ | ○ | なし | ○ |
| HEAD | ヘッダーのみ取得(GETと同じだがボディなし) | ○ | ○ | なし | ○ |
| POST | リソースの作成/データの送信 | × | × | あり | 条件付き |
| PUT | リソースの完全な置換 | × | ○ | あり | × |
| PATCH | リソースの部分的な更新 | × | × | あり | × |
| DELETE | リソースの削除 | × | ○ | 任意 | × |
| OPTIONS | サーバーがサポートするメソッドの問い合わせ | ○ | ○ | なし | × |
| TRACE | リクエストのループバック診断 | ○ | ○ | なし | × |
| CONNECT | トンネルの確立(プロキシ用) | × | × | なし | × |
GET — リソースの取得
# curl での GET リクエスト
curl -v https://api.example.com/users/123
# HTTPie での GET リクエスト
http GET https://api.example.com/users/123
# 生のHTTPリクエスト
GET /users/123 HTTP/1.1
Host: api.example.com
Accept: application/json
GETリクエストは安全(サーバーの状態を変更しない)かつ冪等(何度実行しても同じ結果)であるため、ブラウザはGETリクエストのレスポンスをキャッシュし、ブックマークや履歴に安全に保存できる。
POST — リソースの作成
# curl での POST リクエスト(JSON)
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "alice@example.com"}'
# curl での POST リクエスト(フォームデータ)
curl -X POST https://api.example.com/login \
-d "username=alice&password=secret"
# curl での POST リクエスト(ファイルアップロード)
curl -X POST https://api.example.com/upload \
-F "file=@/path/to/document.pdf" \
-F "description=Important document"
# HTTPie での POST リクエスト
http POST https://api.example.com/users name="Alice" email="alice@example.com"
PUT — リソースの完全な置換
# curl での PUT リクエスト
curl -X PUT https://api.example.com/users/123 \
-H "Content-Type: application/json" \
-d '{"name": "Alice Updated", "email": "alice_new@example.com", "role": "admin"}'
# 生のHTTPリクエスト
PUT /users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 68
{"name": "Alice Updated", "email": "alice_new@example.com", "role": "admin"}
PUTは冪等であり、同じリクエストを何度送信しても結果は同じになる。リソース全体を置換するため、送信しなかったフィールドはnullやデフォルト値になる可能性がある。
PATCH — リソースの部分更新
# curl での PATCH リクエスト(JSON Merge Patch)
curl -X PATCH https://api.example.com/users/123 \
-H "Content-Type: application/merge-patch+json" \
-d '{"email": "new_email@example.com"}'
# curl での PATCH リクエスト(JSON Patch — RFC 6902)
curl -X PATCH https://api.example.com/users/123 \
-H "Content-Type: application/json-patch+json" \
-d '[
{"op": "replace", "path": "/email", "value": "new_email@example.com"},
{"op": "add", "path": "/phone", "value": "+81-90-1234-5678"}
]'
DELETE — リソースの削除
# curl での DELETE リクエスト
curl -X DELETE https://api.example.com/users/123
# 認証付き DELETE リクエスト
curl -X DELETE https://api.example.com/users/123 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
OPTIONS — CORSプリフライトリクエスト
OPTIONSメソッドは、CORS(Cross-Origin Resource Sharing)のプリフライトリクエストとして特に重要である。
# curl での OPTIONS リクエスト
curl -X OPTIONS https://api.example.com/users \
-H "Origin: https://frontend.example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type, Authorization" \
-v
# サーバーからのレスポンス例
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
HEAD — ヘッダーのみ取得
# ファイルサイズの確認(ダウンロードせずに)
curl -I https://example.com/large-file.zip
# レスポンス例
HTTP/1.1 200 OK
Content-Type: application/zip
Content-Length: 1073741824
Last-Modified: Mon, 01 Jan 2026 00:00:00 GMT
3.3 主要なリクエストヘッダー
一般ヘッダー
# ホスト(HTTP/1.1 で必須)
Host: www.example.com
# 接続管理
Connection: keep-alive
Connection: close
# キャッシュ制御
Cache-Control: no-cache
Cache-Control: max-age=3600
コンテントネゴシエーション
# 受け入れ可能なメディアタイプ
Accept: application/json
Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
# 受け入れ可能な文字セット
Accept-Charset: utf-8, iso-8859-1;q=0.5
# 受け入れ可能なエンコーディング
Accept-Encoding: gzip, deflate, br
# 受け入れ可能な言語
Accept-Language: ja, en-US;q=0.9, en;q=0.8
q値(品質値)は0から1の範囲で、クライアントの優先度を示す。デフォルトは1.0である。
認証ヘッダー
# Basic認証
Authorization: Basic dXNlcjpwYXNzd29yZA==
# Bearer Token(OAuth 2.0 / JWT)
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# API Key(カスタムヘッダー)
X-API-Key: abcdef123456
コンテンツ関連ヘッダー
# リクエストボディのメディアタイプ
Content-Type: application/json; charset=utf-8
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
Content-Type: application/x-www-form-urlencoded
# リクエストボディのサイズ
Content-Length: 256
クライアント情報ヘッダー
# ユーザーエージェント
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
# リファラー(リクエスト元のURL)
Referer: https://www.example.com/search?q=http
# 転送元のIPアドレス(プロキシ経由の場合)
X-Forwarded-For: 203.0.113.50, 70.41.3.18
X-Real-IP: 203.0.113.50
3.4 クエリパラメータとパスパラメータ
RESTful APIにおいて、リソースの識別にはパスパラメータを、リソースのフィルタリングや追加情報の指定にはクエリパラメータを使用する。
# パスパラメータ — 特定のリソースを識別
GET /api/users/123 # ユーザーID 123を取得
GET /api/users/123/orders/456 # ユーザー123の注文456を取得
# クエリパラメータ — フィルタリング、ソート、ページネーション
GET /api/users?role=admin&status=active # フィルタリング
GET /api/users?sort=created_at&order=desc # ソート
GET /api/users?page=2&limit=20 # ページネーション
GET /api/users?fields=id,name,email # フィールド選択
3.5 curlの実践的な使用例
# 詳細なリクエスト/レスポンス情報を表示
curl -v https://api.example.com/users
# レスポンスヘッダーのみ表示
curl -I https://api.example.com/users
# カスタムヘッダーの指定
curl -H "Accept: application/json" \
-H "X-Request-ID: uuid-1234" \
https://api.example.com/users
# Basic認証
curl -u username:password https://api.example.com/protected
# リダイレクトの追跡
curl -L https://example.com/redirect
# タイムアウトの設定
curl --connect-timeout 5 --max-time 30 https://api.example.com/slow-endpoint
# レスポンスの書き込み先を指定
curl -o response.json https://api.example.com/data
curl -O https://example.com/file.zip # URLのファイル名で保存
# HTTPバージョンの指定
curl --http1.1 https://example.com
curl --http2 https://example.com
curl --http3 https://example.com
# プロキシ経由のリクエスト
curl -x http://proxy.example.com:8080 https://api.example.com/data
# 証明書の検証をスキップ(開発環境のみ)
curl -k https://self-signed.example.com
# レスポンス時間の計測
curl -w "\nDNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTLS: %{time_appconnect}s\nTotal: %{time_total}s\n" \
-o /dev/null -s https://example.com
第4章: HTTPレスポンスの詳細
4.1 HTTPレスポンスメッセージの構造
HTTPレスポンスメッセージは、以下の構造を持つ。
┌─────────────────────────────────────────┐
│ ステータスライン │
│ HTTP/1.1 200 OK │
├─────────────────────────────────────────┤
│ レスポンスヘッダー │
│ Content-Type: application/json │
│ Content-Length: 256 │
│ Cache-Control: max-age=3600 │
│ Date: Mon, 11 Apr 2026 12:00:00 GMT │
│ Server: nginx/1.24.0 │
├─────────────────────────────────────────┤
│ 空行(CRLF) │
├─────────────────────────────────────────┤
│ レスポンスボディ │
│ {"id": 123, "name": "Alice", ...} │
└─────────────────────────────────────────┘
ステータスラインのフォーマット: HTTPバージョン SP ステータスコード SP 理由句 CRLF
4.2 ステータスコードの分類
HTTPステータスコードは3桁の数値で、最初の桁によって5つのカテゴリに分類される。
1xx — 情報レスポンス(Informational)
サーバーがリクエストを受信し、処理を継続中であることを示す。
| コード | 名前 | 説明 |
|---|---|---|
| 100 | Continue | リクエストの続行を許可。Expect: 100-continueヘッダーへの応答 |
| 101 | Switching Protocols | プロトコルの切り替え(WebSocketアップグレード等) |
| 102 | Processing | リクエスト処理中(WebDAV) |
| 103 | Early Hints | 最終レスポンスの前にリソースのプリロードヒントを送信 |
# 101 Switching Protocols — WebSocket アップグレード
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
# 103 Early Hints — リソースのプリロード
HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script
2xx — 成功レスポンス(Success)
リクエストが正常に処理されたことを示す。
| コード | 名前 | 説明 |
|---|---|---|
| 200 | OK | リクエスト成功(最も一般的) |
| 201 | Created | リソースが正常に作成された |
| 202 | Accepted | リクエストを受け付けたが、処理は未完了 |
| 204 | No Content | 成功したが、レスポンスボディなし |
| 206 | Partial Content | 範囲リクエストに対する部分的なレスポンス |
# 200 OK — リソース取得成功
HTTP/1.1 200 OK
Content-Type: application/json
{"id": 123, "name": "Alice", "email": "alice@example.com"}
# 201 Created — リソース作成成功
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/users/124
{"id": 124, "name": "Bob", "email": "bob@example.com"}
# 204 No Content — 削除成功(レスポンスボディなし)
HTTP/1.1 204 No Content
# 206 Partial Content — 範囲リクエストへの応答
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-999/5000
Content-Length: 1000
Content-Type: application/octet-stream
3xx — リダイレクションレスポンス(Redirection)
リクエストを完了するために追加のアクションが必要であることを示す。
| コード | 名前 | 説明 |
|---|---|---|
| 301 | Moved Permanently | リソースが恒久的に移動(SEOに影響) |
| 302 | Found | リソースが一時的に移動 |
| 303 | See Other | 別のURIをGETで参照すべき |
| 304 | Not Modified | リソースが変更されていない(キャッシュ利用可) |
| 307 | Temporary Redirect | 一時的リダイレクト(メソッドを維持) |
| 308 | Permanent Redirect | 恒久的リダイレクト(メソッドを維持) |
# 301 Moved Permanently — 恒久的リダイレクト
HTTP/1.1 301 Moved Permanently
Location: https://www.example.com/new-page
# 304 Not Modified — キャッシュの有効確認
HTTP/1.1 304 Not Modified
ETag: "abc123"
Cache-Control: max-age=3600
# 307 vs 302 の違い
# 302: ブラウザがPOSTをGETに変更する可能性がある
# 307: メソッドを必ず維持する
HTTP/1.1 307 Temporary Redirect
Location: https://api.example.com/v2/users
301 vs 308、302 vs 307 の使い分け:
- 301/302: 歴史的な理由でメソッドがGETに変更される可能性がある
- 307/308: メソッドとボディが必ず維持される(推奨)
4xx — クライアントエラーレスポンス(Client Error)
クライアント側のエラーを示す。
| コード | 名前 | 説明 |
|---|---|---|
| 400 | Bad Request | リクエストの構文が不正 |
| 401 | Unauthorized | 認証が必要(未認証) |
| 403 | Forbidden | アクセス権限がない(認証済みだが認可されていない) |
| 404 | Not Found | リソースが存在しない |
| 405 | Method Not Allowed | 許可されていないHTTPメソッド |
| 408 | Request Timeout | リクエストのタイムアウト |
| 409 | Conflict | リソースの競合(楽観的ロック失敗等) |
| 413 | Content Too Large | リクエストボディが大きすぎる |
| 415 | Unsupported Media Type | サポートされていないContent-Type |
| 422 | Unprocessable Content | バリデーションエラー |
| 429 | Too Many Requests | レートリミット超過 |
# 400 Bad Request
HTTP/1.1 400 Bad Request
Content-Type: application/json
{"error": "Invalid JSON", "details": "Unexpected token at position 15"}
# 401 Unauthorized
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"
Content-Type: application/json
{"error": "Authentication required"}
# 404 Not Found
HTTP/1.1 404 Not Found
Content-Type: application/json
{"error": "User not found", "id": 999}
# 429 Too Many Requests
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1712834400
Content-Type: application/json
{"error": "Rate limit exceeded", "retry_after": 60}
5xx — サーバーエラーレスポンス(Server Error)
サーバー側のエラーを示す。
| コード | 名前 | 説明 |
|---|---|---|
| 500 | Internal Server Error | サーバー内部エラー |
| 501 | Not Implemented | 機能が未実装 |
| 502 | Bad Gateway | ゲートウェイ/プロキシが不正なレスポンスを受信 |
| 503 | Service Unavailable | サービスが一時的に利用不可(メンテナンス等) |
| 504 | Gateway Timeout | ゲートウェイ/プロキシがタイムアウト |
# 502 Bad Gateway — バックエンドサーバーの障害
HTTP/1.1 502 Bad Gateway
Content-Type: text/html
<html><body><h1>502 Bad Gateway</h1></body></html>
# 503 Service Unavailable — メンテナンス中
HTTP/1.1 503 Service Unavailable
Retry-After: 3600
Content-Type: application/json
{"error": "Service under maintenance", "expected_recovery": "2026-04-11T14:00:00Z"}
4.3 主要なレスポンスヘッダー
# コンテンツ情報
Content-Type: application/json; charset=utf-8
Content-Length: 1024
Content-Language: ja
# コンテンツエンコーディング
Content-Encoding: gzip
Transfer-Encoding: chunked
# キャッシュ制御
Cache-Control: public, max-age=3600
ETag: "v1.0-abc123"
Last-Modified: Mon, 06 Apr 2026 10:00:00 GMT
Expires: Mon, 11 Apr 2026 13:00:00 GMT
# セキュリティ
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Content-Security-Policy: default-src 'self'
# CORS
Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
# サーバー情報
Server: nginx/1.24.0
Date: Mon, 11 Apr 2026 12:00:00 GMT
# レートリミット
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1712834400
4.4 レスポンスボディの形式
# JSON(最も一般的なAPI形式)
Content-Type: application/json
{"users": [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]}
# XML
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<users><user id="1"><name>Alice</name></user></users>
# HTML
Content-Type: text/html; charset=utf-8
<!DOCTYPE html><html><body><h1>Hello</h1></body></html>
# プレーンテキスト
Content-Type: text/plain
Hello, World!
# バイナリ(画像等)
Content-Type: image/png
Content-Length: 102400
(バイナリデータ)
4.5 Content-Encoding と Transfer-Encoding
Content-Encoding(コンテンツ圧縮)
# リクエスト: クライアントが受け入れ可能な圧縮方式を通知
Accept-Encoding: gzip, deflate, br
# レスポンス: サーバーが使用した圧縮方式を通知
Content-Encoding: gzip
主な圧縮アルゴリズム:
- gzip: 最も広くサポートされている圧縮方式
- deflate: zlibベースの圧縮
- br(Brotli): Googleが開発した新しい圧縮アルゴリズム。gzipより高い圧縮率
# Nginx での圧縮設定
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 256;
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml;
# Brotli 圧縮(ngx_brotli モジュールが必要)
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/json application/javascript;
Transfer-Encoding(転送エンコーディング)
# チャンク転送エンコーディング
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: text/plain
1a\r\n
This is the first chunk\r\n
1c\r\n
This is the second chunk!\r\n
0\r\n
\r\n
チャンク転送エンコーディングは、Content-Lengthが事前にわからない場合(動的コンテンツのストリーミング等)に使用される。各チャンクは、16進数のサイズ値、CRLF、データ、CRLFで構成され、サイズ0のチャンクで終了を示す。
第5章: HTTP/1.1の機能と設定
5.1 持続的接続(Keep-Alive)
HTTP/1.0では、各リクエストごとにTCP接続の確立と切断が行われていた。これは「短命な接続(short-lived connection)」と呼ばれ、TCPの3ウェイハンドシェイクのオーバーヘッドが毎回発生するため、パフォーマンス上の大きな課題であった。
HTTP/1.1では、持続的接続(Persistent Connection)がデフォルトで有効になった。これにより、一つのTCP接続上で複数のHTTPリクエスト/レスポンスをやり取りできるようになった。
HTTP/1.0(持続的接続なし):
TCP接続 → GET /page.html → レスポンス → TCP切断
TCP接続 → GET /style.css → レスポンス → TCP切断
TCP接続 → GET /script.js → レスポンス → TCP切断
(3回のTCPハンドシェイクが必要)
HTTP/1.1(持続的接続あり):
TCP接続 → GET /page.html → レスポンス
→ GET /style.css → レスポンス
→ GET /script.js → レスポンス
→ TCP切断
(1回のTCPハンドシェイクで済む)
# Nginx での Keep-Alive 設定
http {
# クライアントとの Keep-Alive
keepalive_timeout 65; # Keep-Alive タイムアウト(秒)
keepalive_requests 1000; # 1接続あたりの最大リクエスト数
# アップストリームとの Keep-Alive
upstream backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
keepalive 32; # 各ワーカーが保持するアイドル接続数
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection ""; # アップストリームへの Keep-Alive を有効化
}
}
}
# Apache での Keep-Alive 設定
<IfModule mod_headers.c>
# Keep-Alive を有効化
KeepAlive On
# 1接続あたりの最大リクエスト数
MaxKeepAliveRequests 100
# Keep-Alive タイムアウト(秒)
KeepAliveTimeout 5
</IfModule>
5.2 パイプライニング
HTTPパイプライニングは、レスポンスを待たずに複数のリクエストを連続して送信する機能である。理論上はレイテンシの削減に有効だが、以下の問題から実際にはほとんど使われていない。
パイプライニングなし:
リクエスト1 → レスポンス1 → リクエスト2 → レスポンス2 → リクエスト3 → レスポンス3
パイプライニングあり:
リクエスト1 → リクエスト2 → リクエスト3 → レスポンス1 → レスポンス2 → レスポンス3
パイプライニングの問題点:
- Head-of-Line Blocking: 先頭のリクエストの処理が遅いと、後続のレスポンスもブロックされる
- 互換性の問題: 多くのプロキシやサーバーが正しくパイプライニングを処理できない
- 実装の複雑さ: エラー処理やリトライのロジックが複雑になる
これらの問題は、HTTP/2のストリーム多重化によって根本的に解決された。
5.3 チャンク転送エンコーディング
チャンク転送エンコーディング(Chunked Transfer Encoding)は、レスポンスの全体サイズが事前にわからない場合に使用される転送方式である。
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
7\r\n
Mozilla\r\n
11\r\n
Developer Network\r\n
0\r\n
\r\n
実際の使用例として、サーバーサイドでのストリーミングレスポンスがある。
# Python Flask でのチャンクレスポンス
from flask import Flask, Response
import time
app = Flask(__name__)
@app.route('/stream')
def stream():
def generate():
for i in range(10):
yield f"data: Event {i}\n\n"
time.sleep(1)
return Response(generate(), mimetype='text/event-stream')
// Node.js でのチャンクレスポンス
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, {
'Content-Type': 'text/plain',
'Transfer-Encoding': 'chunked'
});
let count = 0;
const interval = setInterval(() => {
res.write(`Chunk ${count}\n`);
count++;
if (count >= 10) {
clearInterval(interval);
res.end();
}
}, 1000);
}).listen(8080);
5.4 条件付きリクエスト
条件付きリクエストは、キャッシュの効率的な利用とネットワーク帯域の節約に不可欠な仕組みである。
ETag と If-None-Match
ETag(Entity Tag)は、リソースの特定バージョンを識別するための不透明な文字列である。
# 初回リクエスト
GET /api/users/123 HTTP/1.1
Host: api.example.com
# 初回レスポンス(ETag付き)
HTTP/1.1 200 OK
ETag: "v1-abc123def456"
Content-Type: application/json
{"id": 123, "name": "Alice", "version": 1}
# 2回目以降のリクエスト(条件付き)
GET /api/users/123 HTTP/1.1
Host: api.example.com
If-None-Match: "v1-abc123def456"
# リソースが変更されていない場合(304応答、ボディなし)
HTTP/1.1 304 Not Modified
ETag: "v1-abc123def456"
# リソースが変更された場合(200応答、新しいデータ)
HTTP/1.1 200 OK
ETag: "v2-xyz789ghi012"
Content-Type: application/json
{"id": 123, "name": "Alice Updated", "version": 2}
Last-Modified と If-Modified-Since
# 初回レスポンス
HTTP/1.1 200 OK
Last-Modified: Mon, 06 Apr 2026 10:00:00 GMT
Content-Type: text/html
# 条件付きリクエスト
GET /index.html HTTP/1.1
Host: www.example.com
If-Modified-Since: Mon, 06 Apr 2026 10:00:00 GMT
# リソースが変更されていない場合
HTTP/1.1 304 Not Modified
楽観的ロックへの応用
ETagは、RESTful APIにおける楽観的ロック(Optimistic Locking)の実装にも使用される。
# リソースの取得
GET /api/documents/1 HTTP/1.1
HTTP/1.1 200 OK
ETag: "version-5"
{"id": 1, "title": "Document", "content": "..."}
# 条件付き更新(ETagが一致する場合のみ更新)
PUT /api/documents/1 HTTP/1.1
If-Match: "version-5"
Content-Type: application/json
{"title": "Updated Document", "content": "..."}
# ETagが一致する場合(更新成功)
HTTP/1.1 200 OK
ETag: "version-6"
# ETagが一致しない場合(競合)
HTTP/1.1 412 Precondition Failed
5.5 コンテントネゴシエーション
コンテントネゴシエーションは、同じURIのリソースに対して、クライアントの要求に応じて異なる表現(形式、言語、エンコーディング)を返す仕組みである。
# クライアントの優先度指定
GET /api/data HTTP/1.1
Host: api.example.com
Accept: application/json, application/xml;q=0.9, text/html;q=0.8
Accept-Language: ja, en-US;q=0.9, en;q=0.8
Accept-Encoding: br, gzip;q=0.9, deflate;q=0.5
Accept-Charset: utf-8, iso-8859-1;q=0.5
# サーバーのレスポンス(選択された表現)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Language: ja
Content-Encoding: br
Vary: Accept, Accept-Language, Accept-Encoding
Vary ヘッダーは、キャッシュに対して、同じURLでもどのリクエストヘッダーの値によって異なるレスポンスが返されるかを通知する。
5.6 Nginx での HTTP/1.1 設定例
# /etc/nginx/nginx.conf — HTTP/1.1 最適化設定
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
multi_accept on;
use epoll;
}
http {
# 基本設定
sendfile on;
tcp_nopush on;
tcp_nodelay on;
types_hash_max_size 2048;
server_tokens off; # サーバーバージョンの非表示
# Keep-Alive 設定
keepalive_timeout 65;
keepalive_requests 1000;
# バッファ設定
client_body_buffer_size 16k;
client_header_buffer_size 1k;
client_max_body_size 10m;
large_client_header_buffers 4 8k;
# タイムアウト設定
client_body_timeout 12;
client_header_timeout 12;
send_timeout 10;
# Gzip 圧縮
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 256;
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml
font/woff2;
# ログフォーマット
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
server {
listen 80;
server_name www.example.com;
# HTTPSへのリダイレクト
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name www.example.com;
# SSL設定
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# セキュリティヘッダー
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# 静的ファイルの配信
location /static/ {
alias /var/www/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
# APIプロキシ
location /api/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
}
}
}
5.7 Apache での HTTP/1.1 設定例
# /etc/apache2/apache2.conf — HTTP/1.1 最適化設定
ServerTokens Prod
ServerSignature Off
Timeout 60
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
# MPM Event 設定
<IfModule mpm_event_module>
StartServers 3
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 0
</IfModule>
# 圧縮設定
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml
AddOutputFilterByType DEFLATE text/css text/javascript
AddOutputFilterByType DEFLATE application/javascript application/json
AddOutputFilterByType DEFLATE application/xml image/svg+xml
DeflateCompressionLevel 6
</IfModule>
# キャッシュ設定
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
</IfModule>
# セキュリティヘッダー
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set Content-Security-Policy "default-src 'self'"
</IfModule>
# バーチャルホスト設定
<VirtualHost *:443>
ServerName www.example.com
DocumentRoot /var/www/html
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
# リバースプロキシ設定
ProxyPreserveHost On
ProxyPass /api/ http://127.0.0.1:8080/api/
ProxyPassReverse /api/ http://127.0.0.1:8080/api/
</VirtualHost>
第6章: HTTP/2の機能と設定
6.1 HTTP/2の背景
HTTP/2は、2015年にRFC 7540として標準化されたHTTPの第2世代プロトコルである。GoogleのSPDY(スピーディ)プロトコルを基盤として開発され、HTTP/1.1のパフォーマンス上の課題を解決することを目的としている。
HTTP/1.1の主な課題:
- Head-of-Line Blocking: 1つの接続で同時に1つのリクエストしか処理できない
- ヘッダーの冗長性: 毎回同じヘッダーが繰り返し送信される
- テキストベースプロトコル: パース処理のオーバーヘッド
- クライアント主導の通信のみ: サーバーからの事前送信ができない
6.2 バイナリフレーミング層
HTTP/2の最も根本的な変更点は、テキストベースのプロトコルからバイナリフレーミング層への移行である。
HTTP/1.1(テキストベース):
GET /index.html HTTP/1.1\r\n
Host: www.example.com\r\n
Accept: text/html\r\n
\r\n
HTTP/2(バイナリフレーム):
┌──────────────────────────────────────┐
│ Length (24bit) │ Type (8bit) │ Flags │
│ (8bit) │
├──────────────────────────────────────┤
│ R │ Stream Identifier (31bit) │
├──────────────────────────────────────┤
│ Frame Payload (可変長) │
└──────────────────────────────────────┘
主なフレームタイプ:
| タイプ | 値 | 説明 |
|---|---|---|
| DATA | 0x0 | リクエスト/レスポンスのボディデータ |
| HEADERS | 0x1 | ヘッダーフィールドのブロック |
| PRIORITY | 0x2 | ストリームの優先度(HTTP/2のみ。RFC 9113では非推奨) |
| RST_STREAM | 0x3 | ストリームの即時終了 |
| SETTINGS | 0x4 | 接続設定パラメータ |
| PUSH_PROMISE | 0x5 | サーバープッシュの予告 |
| PING | 0x6 | 接続の生存確認とRTT計測 |
| GOAWAY | 0x7 | 接続の終了通知 |
| WINDOW_UPDATE | 0x8 | フロー制御ウィンドウの更新 |
| CONTINUATION | 0x9 | HEADERSフレームの継続 |
6.3 ストリーム多重化(Multiplexing)
ストリーム多重化は、HTTP/2の最も重要な機能である。単一のTCP接続上で複数のリクエスト/レスポンスを並行して処理できる。
HTTP/1.1(6本の並列TCP接続が必要):
TCP接続1: GET /page.html ──→ レスポンス
TCP接続2: GET /style.css ──→ レスポンス
TCP接続3: GET /app.js ──→ レスポンス
TCP接続4: GET /logo.png ──→ レスポンス
TCP接続5: GET /font.woff ──→ レスポンス
TCP接続6: GET /api/data ──→ レスポンス
HTTP/2(1本のTCP接続で全て処理):
┌── Stream 1: HEADERS(/page.html) → DATA
├── Stream 3: HEADERS(/style.css) → DATA
単一TCP ─┤── Stream 5: HEADERS(/app.js) → DATA
接続 ├── Stream 7: HEADERS(/logo.png) → DATA
├── Stream 9: HEADERS(/font.woff) → DATA
└── Stream 11: HEADERS(/api/data) → DATA
各ストリームの特徴:
- ストリームは双方向のフレームシーケンスである
- クライアント発のストリームは奇数ID、サーバー発は偶数ID
- ストリームは独立しており、互いにブロックしない
- ストリームは優先度を持ち、リソース割り当てを制御できる
6.4 ヘッダー圧縮(HPACK)
HTTP/1.1では、同じヘッダー(Host、User-Agent、Cookie等)が毎回繰り返し送信されていた。HTTP/2ではHPACK(RFC 7541)による効率的なヘッダー圧縮が行われる。
HPACKの仕組み:
- 静的テーブル: 61個の事前定義されたヘッダーフィールド(
:method: GET,:status: 200等) - 動的テーブル: 接続中に学習したヘッダーフィールドのキャッシュ
- ハフマン符号化: ヘッダー値のバイナリ圧縮
# HTTP/1.1 での2回のリクエストヘッダー(毎回全送信)
# リクエスト1: 約400バイト
GET /page.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language: ja,en-US;q=0.9
Accept-Encoding: gzip, deflate, br
Cookie: session=abc123def456; preferences=dark_mode
# リクエスト2: 約400バイト(ほぼ同じヘッダーを再送信)
GET /style.css HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...
Accept: text/css,*/*;q=0.1
Accept-Language: ja,en-US;q=0.9
Accept-Encoding: gzip, deflate, br
Cookie: session=abc123def456; preferences=dark_mode
# HTTP/2 でのHPACK圧縮後
# リクエスト1: 圧縮後約150バイト
# リクエスト2: 差分のみ約30バイト(:path と accept のみ変更)
6.5 サーバープッシュ
サーバープッシュは、クライアントが明示的に要求する前に、サーバーがリソースを先行して送信する機能である。
通常のHTTP通信:
クライアント → GET /index.html
サーバー ← 200 OK (HTML)
クライアント → GET /style.css ← HTMLを解析して初めてCSSの必要性を認識
サーバー ← 200 OK (CSS)
クライアント → GET /app.js ← HTMLを解析して初めてJSの必要性を認識
サーバー ← 200 OK (JS)
サーバープッシュ:
クライアント → GET /index.html
サーバー ← PUSH_PROMISE: /style.css ← HTMLと同時にCSSとJSをプッシュ
サーバー ← PUSH_PROMISE: /app.js
サーバー ← 200 OK (HTML)
サーバー ← 200 OK (CSS) ← プッシュされたリソース
サーバー ← 200 OK (JS) ← プッシュされたリソース
注意: サーバープッシュはHTTP/2の目玉機能として注目されたが、実際の運用では期待したほどのパフォーマンス改善が得られず、誤用によりかえってパフォーマンスが低下するケースも多かった。Chrome 106(2022年9月)でサーバープッシュのサポートが削除され、HTTP/3(RFC 9114)でも非推奨となっている。代替として103 Early Hintsの使用が推奨されている。
# Nginx でのサーバープッシュ設定(参考)
server {
listen 443 ssl http2;
server_name www.example.com;
location = /index.html {
http2_push /css/style.css;
http2_push /js/app.js;
http2_push /images/logo.png;
}
}
# 103 Early Hints の設定(推奨される代替手段)
server {
listen 443 ssl http2;
server_name www.example.com;
location = /index.html {
add_header Link "</css/style.css>; rel=preload; as=style" early;
add_header Link "</js/app.js>; rel=preload; as=script" early;
}
}
6.6 フロー制御
HTTP/2は、接続レベルとストリームレベルの2段階のフロー制御を実装している。
接続レベルのフロー制御:
┌────────────────────────────────────────────────┐
│ TCP接続全体のウィンドウサイズ: 65,535 バイト │
│ ├── Stream 1: ウィンドウ 32,768 バイト │
│ ├── Stream 3: ウィンドウ 16,384 バイト │
│ └── Stream 5: ウィンドウ 16,383 バイト │
└────────────────────────────────────────────────┘
フロー制御の仕組み:
- 各ストリームと接続にはウィンドウサイズがある(デフォルト65,535バイト)
- DATAフレーム送信ごとにウィンドウサイズが減少する
- 受信側がWINDOW_UPDATEフレームを送信してウィンドウを拡大する
- ウィンドウサイズが0になると、WINDOW_UPDATEを受信するまでデータ送信は停止する
6.7 Nginx での HTTP/2 設定例
# /etc/nginx/conf.d/http2.conf
server {
# HTTP/2 を有効化(Nginx 1.25.1以降では listen ディレクティブに http2 を指定)
listen 443 ssl;
http2 on;
server_name www.example.com;
# SSL/TLS 設定(HTTP/2 にはTLSが実質必須)
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
# HTTP/2 固有の設定
# 同時ストリーム数の制限
http2_max_concurrent_streams 128;
# アップストリーム設定(HTTP/2でバックエンドに接続する場合)
location / {
proxy_pass https://backend;
proxy_http_version 1.1; # バックエンドへの接続はHTTP/1.1が一般的
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
6.8 Apache での HTTP/2 設定例
# HTTP/2 モジュールの有効化
# sudo a2enmod http2
<VirtualHost *:443>
ServerName www.example.com
# HTTP/2 を有効化
Protocols h2 h2c http/1.1
# SSL 設定
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
# HTTP/2 固有の設定
H2Push on
H2PushPriority css after
H2PushPriority js after
H2PushPriority image after
# 同時ストリーム数
H2MaxSessionStreams 100
# ウィンドウサイズ
H2WindowSize 65535
DocumentRoot /var/www/html
# プッシュリソースの指定
<Location /index.html>
Header add Link "</css/style.css>;rel=preload;as=style"
Header add Link "</js/app.js>;rel=preload;as=script"
</Location>
</VirtualHost>
6.9 HTTP/2 の接続確立
HTTP/2の接続確立には2つの方法がある。
ALPN(Application-Layer Protocol Negotiation)
TLSハンドシェイク中にHTTP/2の使用をネゴシエーションする。これが最も一般的な方法である。
ClientHello:
ALPN: h2, http/1.1
ServerHello:
ALPN: h2
→ HTTP/2 接続が確立される
HTTP Upgrade(h2c — HTTP/2 over cleartext)
暗号化なしのHTTP/2接続を確立する場合に使用される。
GET / HTTP/1.1
Host: www.example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: <base64url エンコードされたSETTINGSフレーム>
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c
→ 以降 HTTP/2 フレームで通信
# curl で HTTP/2 接続を確認
curl -v --http2 https://www.example.com 2>&1 | grep -i "http/2"
# nghttp による HTTP/2 通信のデバッグ
nghttp -v https://www.example.com
第7章: HTTP/3とQUIC
7.1 QUICプロトコルの概要
QUIC(Quick UDP Internet Connections)は、Googleが2012年に開発を開始し、IETFで標準化されたトランスポートプロトコルである(RFC 9000, 2021年)。QUICはUDP上に構築されており、TCP + TLSの機能を統合した新しいトランスポート層を提供する。
プロトコルスタックの比較:
HTTP/1.1 & HTTP/2: HTTP/3:
┌──────────────┐ ┌──────────────┐
│ HTTP/1.1 │ │ HTTP/3 │
│ HTTP/2 │ │ │
├──────────────┤ ├──────────────┤
│ TLS 1.2 │ │ QUIC │
│ TLS 1.3 │ │ (TLS 1.3 │
├──────────────┤ │ 内蔵) │
│ TCP │ ├──────────────┤
├──────────────┤ │ UDP │
│ IP │ ├──────────────┤
└──────────────┘ │ IP │
└──────────────┘
QUICの主な特徴:
- 暗号化が標準: TLS 1.3が組み込まれており、暗号化は必須
- 多重化: ストリームレベルでの多重化をトランスポート層で実現
- 接続のマイグレーション: IPアドレスの変更に対応
- 改善されたロス回復: より効率的なパケットロスの検出と回復
- ユーザースペースでの実装: カーネルの更新なしに改善可能
7.2 HTTP/3の特徴と利点
HTTP/3(RFC 9114)は、QUICトランスポート上に構築されたHTTPの最新バージョンである。
Head-of-Line Blocking の完全な解消
HTTP/2ではTCP上で多重化を行うため、TCPレベルでのパケットロスが発生すると、全てのストリームがブロックされるという問題(TCP Head-of-Line Blocking)があった。HTTP/3ではQUICがストリームごとに独立したロス回復を行うため、この問題が完全に解消された。
HTTP/2 (TCP) でのパケットロス:
Stream 1: [フレーム1] [フレーム2] [フレーム3 ✗ロスト]
Stream 2: [フレーム1] [フレーム2] ← Stream 1のロスにより全ストリームがブロック
Stream 3: [フレーム1] [フレーム2] ← ブロック
HTTP/3 (QUIC) でのパケットロス:
Stream 1: [フレーム1] [フレーム2] [フレーム3 ✗ロスト] ← このストリームのみ再送待ち
Stream 2: [フレーム1] [フレーム2] [フレーム3] ← 影響なし、正常に処理
Stream 3: [フレーム1] [フレーム2] [フレーム3] ← 影響なし、正常に処理
7.3 0-RTT接続確立
QUICは、以前接続したサーバーへの再接続において、0-RTT(Zero Round Trip Time)接続確立をサポートする。
初回接続(1-RTT):
クライアント サーバー
| ---- Initial (ClientHello) ----→ |
| ←--- Initial (ServerHello) ----- | 1 RTT
| ---- Handshake (Finished) -----→ |
| ←--- Handshake (Finished) ------ |
| ---- HTTP リクエスト -----------→ |
| ←--- HTTP レスポンス ----------- |
再接続(0-RTT):
クライアント サーバー
| ---- Initial + 0-RTT データ ---→ | 0 RTT!
| (ClientHello + HTTPリクエスト) | (最初のパケットに
| ←--- Initial + レスポンス ------ | データを含められる)
比較:
TCP + TLS 1.2: 3 RTT(TCP: 1.5 RTT + TLS: 1.5 RTT)
TCP + TLS 1.3: 2 RTT(TCP: 1.5 RTT + TLS: 0.5 RTT)
QUIC 初回: 1 RTT
QUIC 再接続: 0 RTT
0-RTTのセキュリティ上の注意: 0-RTTデータはリプレイ攻撃に対して脆弱であるため、冪等なリクエスト(GETなど)にのみ使用すべきである。状態を変更するリクエスト(POST等)には使用しないことが推奨される。
7.4 コネクションマイグレーション
従来のTCPベースのHTTPでは、クライアントのIPアドレスが変更されると(例:Wi-FiからLTEへの切り替え)、TCP接続が切断され、再確立が必要であった。QUICはコネクションIDに基づいて接続を管理するため、IPアドレスが変わっても接続を維持できる。
従来(TCP):
Wi-Fi (192.168.1.10) → TCP接続確立 → 通信中
↓ (電車で移動、Wi-Fiの範囲外に)
LTE (100.64.0.50) → TCP接続が切断 → 再接続が必要(3-way handshake + TLS)
QUIC:
Wi-Fi (192.168.1.10) → QUIC接続確立 (Connection ID: 0xabc123) → 通信中
↓ (電車で移動、Wi-Fiの範囲外に)
LTE (100.64.0.50) → 同じConnection IDで通信継続 → シームレスに切り替え
7.5 QPACK — HTTP/3のヘッダー圧縮
HTTP/2のHPACKはストリーム間の順序依存性があったため、QUICの独立したストリーム配信と相性が悪かった。HTTP/3ではQPACK(RFC 9204)という新しいヘッダー圧縮方式を採用している。
QPACKの特徴:
- 動的テーブルの更新に専用のユニ方向ストリームを使用
- ストリーム間の順序依存性を最小化
- エンコーダーとデコーダーの同期メカニズム
- HPACKの静的テーブルを拡張(91エントリ)
7.6 HTTP/2との詳細比較
| 特性 | HTTP/2 | HTTP/3 |
|---|---|---|
| トランスポート | TCP | QUIC (UDP) |
| 暗号化 | オプション(実質TLS必須) | 必須(TLS 1.3内蔵) |
| 多重化 | ストリーム多重化 | ストリーム多重化 |
| HoL Blocking | TCP層で発生する | 完全に解消 |
| ヘッダー圧縮 | HPACK | QPACK |
| 接続確立 | TCP + TLS(2-3 RTT) | 1 RTT(再接続: 0 RTT) |
| コネクションマイグレーション | なし | あり |
| サーバープッシュ | あり(非推奨化) | あり(非推奨) |
| フロー制御 | ストリーム+接続レベル | ストリーム+接続レベル |
| NAT/ファイアウォール互換性 | 問題なし | UDPブロックの可能性あり |
| 実装の成熟度 | 高い | 成長中 |
7.7 Nginx での HTTP/3 設定例
# Nginx 1.25.0+ で HTTP/3 (QUIC) を有効化
# コンパイル時に --with-http_v3_module が必要
server {
# HTTP/3 (QUIC) 用のUDPリスナー
listen 443 quic reuseport;
# HTTP/2 用のTCPリスナー(フォールバック)
listen 443 ssl;
http2 on;
server_name www.example.com;
# SSL/TLS 設定
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# QUIC 固有の設定
ssl_early_data on; # 0-RTT を有効化
# Alt-Svc ヘッダーでHTTP/3対応を通知
add_header Alt-Svc 'h3=":443"; ma=86400' always;
# QUIC用のトランスポートパラメータ
quic_retry on; # アドレス検証を有効化
location / {
root /var/www/html;
index index.html;
}
}
7.8 Caddy での HTTP/3 設定例
Caddyは、HTTP/3をデフォルトでサポートしているWebサーバーである。
# Caddyfile
www.example.com {
# HTTP/3 はデフォルトで有効
# 自動HTTPS(Let's Encryptの証明書を自動取得)
root * /var/www/html
file_server
# 明示的にHTTP/3を設定する場合
servers {
protocols h1 h2 h3
}
# リバースプロキシ
reverse_proxy /api/* {
to localhost:8080
header_up X-Real-IP {remote_host}
}
# 圧縮
encode gzip zstd
# ログ
log {
output file /var/log/caddy/access.log
format json
}
}
7.9 Cloudflare での HTTP/3 有効化
Cloudflareは、HTTP/3を簡単に有効化できるCDNサービスである。
# Cloudflare API で HTTP/3 を有効化
curl -X PATCH "https://api.cloudflare.com/client/v4/zones/{zone_id}/settings/http3" \
-H "Authorization: Bearer {api_token}" \
-H "Content-Type: application/json" \
--data '{"value": "on"}'
7.10 HTTP/3のデバッグ
# curl で HTTP/3 接続を確認(curl 7.86.0+ が必要)
curl --http3 -v https://www.example.com 2>&1 | head -20
# HTTP/3 が利用可能かテスト(フォールバック付き)
curl --http3-only https://www.example.com # HTTP/3のみ
curl --http3 https://www.example.com # HTTP/3を試み、失敗時はHTTP/2にフォールバック
# quiche-client による詳細なデバッグ
quiche-client https://www.example.com --dump-packets
# Chrome DevTools での確認
# Network タブ → Protocol 列で "h3" を確認
7.11 HTTP/3導入時の考慮事項
- UDPのブロック: 企業のファイアウォールでUDPポート443がブロックされている場合がある。HTTP/2へのフォールバック機構が必要
- UDPの処理能力: サーバーやネットワーク機器のUDP処理能力がTCPより低い場合がある
- CPUオーバーヘッド: QUICの暗号化処理はカーネルではなくユーザースペースで実行されるため、CPU使用率が高くなる可能性がある
- モニタリング: 既存のTCP監視ツールがQUIC/UDPに対応していない場合がある
- Alt-Svc: クライアントにHTTP/3対応を通知するため、Alt-Svcヘッダーを正しく設定する必要がある
第8章: HTTPセキュリティ
8.1 HTTPS / TLSの仕組み
HTTPS(HTTP Secure)は、HTTP通信をTLS(Transport Layer Security)で暗号化したプロトコルである。TLSは、通信の機密性(Confidentiality)、完全性(Integrity)、認証(Authentication) の3つを保証する。
HTTP: http://www.example.com (ポート 80) — 平文通信
HTTPS: https://www.example.com (ポート 443) — 暗号化通信
TLSが提供する保証:
- 機密性: 通信内容の暗号化(盗聴の防止)
- 完全性: データの改ざん検知(MACによるメッセージ認証)
- 認証: サーバー(およびオプションでクライアント)のアイデンティティ検証
8.2 TLSハンドシェイクの詳細
TLS 1.2 ハンドシェイク(2 RTT)
クライアント サーバー
| ---- ClientHello ----------------------→ |
| (サポートする暗号スイート、乱数) |
| |
| ←--- ServerHello ----------------------- |
| ServerCertificate | 1 RTT
| ServerKeyExchange |
| ServerHelloDone |
| |
| ---- ClientKeyExchange ----------------→ |
| ChangeCipherSpec | 2 RTT
| Finished |
| |
| ←--- ChangeCipherSpec ------------------ |
| Finished |
| |
| ==== 暗号化通信開始 =================== |
TLS 1.3 ハンドシェイク(1 RTT)
クライアント サーバー
| ---- ClientHello ----------------------→ |
| (サポートするグループ、鍵共有) |
| |
| ←--- ServerHello ----------------------- |
| EncryptedExtensions | 1 RTT
| Certificate |
| CertificateVerify |
| Finished |
| |
| ---- Finished -------------------------→ |
| |
| ==== 暗号化通信開始(0.5 RTT早い)===== |
TLS 1.3の主な改善点:
- ハンドシェイクが1 RTTに短縮
- 0-RTTモードのサポート
- 脆弱な暗号スイートの廃止(RSA鍵交換、CBC、RC4等)
- ハンドシェイクの大部分が暗号化される(プライバシー向上)
- 前方秘匿性(Forward Secrecy)が必須
8.3 証明書の種類と取得方法
証明書の種類
| 種類 | 検証レベル | 特徴 | 用途 |
|---|---|---|---|
| DV(Domain Validation) | ドメイン所有権のみ | 最も安価・簡単 | 個人サイト、ブログ |
| OV(Organization Validation) | 組織の実在確認 | 組織名が証明書に記載 | 企業サイト |
| EV(Extended Validation) | 厳格な組織審査 | アドレスバーに組織名表示 | 金融機関、ECサイト |
Let's Encrypt での無料証明書の取得
# certbot のインストール(Ubuntu/Debian)
sudo apt update
sudo apt install certbot python3-certbot-nginx
# Nginx 用の証明書取得(自動設定)
sudo certbot --nginx -d example.com -d www.example.com
# スタンドアロンモード(Webサーバーを一時停止して取得)
sudo certbot certonly --standalone -d example.com
# DNS認証(ワイルドカード証明書の取得)
sudo certbot certonly --manual --preferred-challenges dns \
-d "*.example.com" -d example.com
# 証明書の自動更新テスト
sudo certbot renew --dry-run
# crontab による自動更新
# /etc/cron.d/certbot
0 0,12 * * * root certbot renew --quiet --deploy-hook "systemctl reload nginx"
8.4 HSTS(HTTP Strict Transport Security)
HSTSは、ブラウザに対してHTTPSのみでアクセスするよう指示するセキュリティ機構である。
# HSTS ヘッダー
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
max-age: ブラウザがHTTPS接続を強制する期間(秒)includeSubDomains: サブドメインにも適用preload: ブラウザのHSTSプリロードリストへの登録申請に必要
# Nginx での HSTS 設定
server {
listen 443 ssl;
server_name www.example.com;
# HSTS(2年間)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# HTTP から HTTPS へのリダイレクト
if ($scheme != "https") {
return 301 https://$server_name$request_uri;
}
}
8.5 CSP(Content Security Policy)
CSPは、XSS(Cross-Site Scripting)やデータインジェクション攻撃を防ぐためのセキュリティヘッダーである。
# 基本的な CSP
Content-Security-Policy: default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' https://fonts.googleapis.com;
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self'
# レポートのみモード(違反を検知するが、ブロックしない)
Content-Security-Policy-Report-Only: default-src 'self';
report-uri /csp-report;
report-to csp-endpoint
# Nginx での CSP 設定
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-$request_id'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://api.example.com; frame-ancestors 'none'" always;
8.6 CORS(Cross-Origin Resource Sharing)
CORSは、異なるオリジン間でのリソース共有を安全に制御する仕組みである。
同一オリジン(Same Origin):
https://www.example.com/page1 → https://www.example.com/api/data ✓ 許可
異なるオリジン(Cross Origin):
https://www.example.com/page1 → https://api.example.com/data ✗ ブロック(CORSなし)
https://www.example.com/page1 → https://api.example.com/data ✓ 許可(CORS設定済み)
CORSプリフライトリクエスト
# 単純リクエスト(Simple Request): プリフライト不要
# 条件: GET/HEAD/POST、特定のヘッダーのみ、特定のContent-Typeのみ
# プリフライト付きリクエスト
# Step 1: ブラウザが自動的にOPTIONSリクエストを送信
OPTIONS /api/data HTTP/1.1
Host: api.example.com
Origin: https://www.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization
# Step 2: サーバーが許可情報を返す
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Access-Control-Allow-Credentials: true
# Step 3: 実際のリクエストを送信
PUT /api/data HTTP/1.1
Host: api.example.com
Origin: https://www.example.com
Content-Type: application/json
Authorization: Bearer token123
{"key": "value"}
# Nginx での CORS 設定
location /api/ {
# プリフライトリクエストへの応答
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://www.example.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Length' 0;
return 204;
}
# 通常のリクエストへのCORSヘッダー付与
add_header 'Access-Control-Allow-Origin' 'https://www.example.com' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
proxy_pass http://backend;
}
8.7 主要なセキュリティヘッダー一覧
# Nginx — 推奨セキュリティヘッダー設定
server {
# HTTPS の強制
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# XSS保護
add_header X-Content-Type-Options "nosniff" always;
# クリックジャッキング防止
add_header X-Frame-Options "SAMEORIGIN" always;
# CSP
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" always;
# Referrer 情報の制限
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 権限ポリシー(旧 Feature-Policy)
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# クロスオリジン分離
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
}
8.8 OWASP Top 10 に関連するHTTPセキュリティ対策
| OWASP Top 10 | HTTPレベルの対策 |
|---|---|
| A01: アクセス制御の不備 | 適切な認証/認可ヘッダー、CORSの厳格な設定 |
| A02: 暗号化の失敗 | HTTPS必須、HSTS、TLS 1.2以上、強力な暗号スイート |
| A03: インジェクション | Content-Type の厳格な検証、CSP、入力バリデーション |
| A05: セキュリティ設定ミス | セキュリティヘッダーの設定、不要なHTTPメソッドの無効化 |
| A07: 認証の不備 | セキュアなCookie設定、適切なセッション管理 |
| A09: ログとモニタリングの不足 | アクセスログの適切な記録と監視 |
# 不要なHTTPメソッドの制限
if ($request_method !~ ^(GET|HEAD|POST|PUT|PATCH|DELETE)$) {
return 405;
}
# セキュアなCookie設定例(アプリケーション側)
Set-Cookie: session_id=abc123; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
第9章: キャッシュ制御
9.1 HTTPキャッシュの概要
HTTPキャッシュは、以前取得したリソースのコピーを保存し、再利用することで、ネットワーク帯域の節約、レイテンシの削減、サーバー負荷の軽減を実現する仕組みである。
キャッシュの階層:
ブラウザ CDN/エッジ リバースプロキシ オリジンサーバー
キャッシュ キャッシュ キャッシュ
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│ ユーザー│ ──→ │ CDN │ ──→ │Varnish│ ──→ │ Nginx │
│ブラウザ │ ←── │ Edge │ ←── │ │ ←── │ App │
└──────┘ └──────┘ └──────┘ └──────┘
Private Shared Shared Origin
Cache Cache Cache
9.2 Cache-Control ヘッダーの詳細
Cache-Controlヘッダーは、HTTPキャッシュ制御の中核となるヘッダーである(RFC 9111)。
レスポンスのCache-Controlディレクティブ
# パブリックキャッシュ(CDN、プロキシで共有可能)— 1時間
Cache-Control: public, max-age=3600
# プライベートキャッシュ(ブラウザのみ)— 10分
Cache-Control: private, max-age=600
# キャッシュ禁止(機密情報、個人データ)
Cache-Control: no-store
# キャッシュは保持するが、毎回サーバーに検証を要求
Cache-Control: no-cache
# キャッシュは保持し、有効期限切れ後にサーバーに検証を要求
Cache-Control: must-revalidate, max-age=3600
# CDNでのキャッシュ期間を別途指定
Cache-Control: public, max-age=60, s-maxage=3600
# 不変なリソース(バージョニングされたアセット)
Cache-Control: public, max-age=31536000, immutable
# stale-while-revalidate: バックグラウンドで再検証しつつ古いキャッシュを提供
Cache-Control: public, max-age=60, stale-while-revalidate=300
# stale-if-error: オリジンサーバーエラー時に古いキャッシュを提供
Cache-Control: public, max-age=60, stale-if-error=86400
リクエストのCache-Controlディレクティブ
# キャッシュを使わずにサーバーから取得
Cache-Control: no-cache
# キャッシュされたレスポンスのみ使用(ネットワークに接続しない)
Cache-Control: only-if-cached
# 指定した秒数以内のキャッシュのみ受け入れる
Cache-Control: max-age=0
# 古いキャッシュでも受け入れる(最大60秒遅れ)
Cache-Control: max-stale=60
9.3 ETag / Last-Modified による検証
キャッシュの有効期限が切れた場合、条件付きリクエストにより、リソースが変更されたかどうかを効率的に確認できる。
強いETag vs 弱いETag
# 強いETag — バイト単位で完全一致を保証
ETag: "abc123def456"
# 弱いETag — 意味的に同等であることを保証
ETag: W/"abc123def456"
キャッシュ検証のフロー
1. 初回リクエスト
GET /api/data HTTP/1.1
HTTP/1.1 200 OK
Cache-Control: max-age=60
ETag: "v1-abc123"
Last-Modified: Mon, 06 Apr 2026 10:00:00 GMT
Content-Type: application/json
{"data": "..."}
2. 60秒以内の再リクエスト → キャッシュから即座に返す(ネットワーク不要)
3. 60秒経過後の再リクエスト → 条件付きリクエストで検証
GET /api/data HTTP/1.1
If-None-Match: "v1-abc123"
If-Modified-Since: Mon, 06 Apr 2026 10:00:00 GMT
# リソースが変更されていない場合(帯域の節約)
HTTP/1.1 304 Not Modified
ETag: "v1-abc123"
Cache-Control: max-age=60
# リソースが変更された場合
HTTP/1.1 200 OK
ETag: "v2-xyz789"
Cache-Control: max-age=60
{"data": "updated..."}
9.4 リソース種別ごとのキャッシュ戦略
# Nginx でのリソース種別ごとのキャッシュ設定
server {
listen 443 ssl;
server_name www.example.com;
# HTML — 短いキャッシュ + 再検証
location ~* \.html$ {
add_header Cache-Control "no-cache";
etag on;
}
# バージョニングされたアセット(CSS/JS)— 長期キャッシュ
# 例: /assets/style.a1b2c3d4.css
location ~* \.[0-9a-f]{8}\.(css|js)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
# 画像/フォント — 1ヶ月キャッシュ
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|woff2|woff|ttf)$ {
add_header Cache-Control "public, max-age=2592000";
}
# API レスポンス — キャッシュなし
location /api/ {
add_header Cache-Control "no-store";
proxy_pass http://backend;
}
# 公開API(変更頻度の低いデータ)
location /api/public/ {
add_header Cache-Control "public, max-age=300, stale-while-revalidate=60";
proxy_pass http://backend;
}
}
9.5 キャッシュ無効化(Cache Busting)
# ファイル名にハッシュを含める(最も一般的)
/assets/style.css → /assets/style.a1b2c3d4.css
/assets/app.js → /assets/app.e5f6g7h8.js
# クエリパラメータにバージョンを含める
/assets/style.css?v=1.2.3
/assets/style.css?t=1712834400
# Webpackでのハッシュ付きファイル名の設定例
module.exports = {
output: {
filename: '[name].[contenthash:8].js',
chunkFilename: '[name].[contenthash:8].chunk.js',
}
};
9.6 Varnish Cache の設定例
Varnishは、高性能なHTTPリバースプロキシキャッシュである。
# /etc/varnish/default.vcl
vcl 4.1;
backend default {
.host = "127.0.0.1";
.port = "8080";
.connect_timeout = 5s;
.first_byte_timeout = 30s;
.between_bytes_timeout = 10s;
# ヘルスチェック
.probe = {
.url = "/health";
.timeout = 3s;
.interval = 5s;
.window = 5;
.threshold = 3;
}
}
sub vcl_recv {
# Cookie のないリクエストはキャッシュ可能にする
if (req.http.Cookie) {
# セッションCookie以外を削除
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(?!session_id=)[^=]+=[^;]*", "");
if (req.http.Cookie == "") {
unset req.http.Cookie;
}
}
# 静的ファイルは常にキャッシュ
if (req.url ~ "\.(css|js|png|jpg|jpeg|gif|ico|svg|woff2)$") {
unset req.http.Cookie;
return (hash);
}
# API は Pass(キャッシュしない)
if (req.url ~ "^/api/") {
return (pass);
}
}
sub vcl_backend_response {
# 静的ファイルのキャッシュ期間を設定
if (bereq.url ~ "\.(css|js|png|jpg|jpeg|gif|ico|svg|woff2)$") {
set beresp.ttl = 7d;
set beresp.http.Cache-Control = "public, max-age=604800";
unset beresp.http.Set-Cookie;
}
# HTMLのキャッシュ期間
if (beresp.http.Content-Type ~ "text/html") {
set beresp.ttl = 5m;
}
# Grace期間(オリジンダウン時に古いキャッシュを提供)
set beresp.grace = 1h;
}
sub vcl_deliver {
# キャッシュヒット/ミスの情報をヘッダーに追加(デバッグ用)
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
set resp.http.X-Cache-Hits = obj.hits;
} else {
set resp.http.X-Cache = "MISS";
}
}
9.7 Cloudflare のキャッシュ設定
# Cloudflare API でのキャッシュ設定
# キャッシュレベルの設定
curl -X PATCH "https://api.cloudflare.com/client/v4/zones/{zone_id}/settings/cache_level" \
-H "Authorization: Bearer {api_token}" \
-H "Content-Type: application/json" \
--data '{"value": "aggressive"}'
# 特定URLのキャッシュパージ
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache" \
-H "Authorization: Bearer {api_token}" \
-H "Content-Type: application/json" \
--data '{
"files": [
"https://www.example.com/style.css",
"https://www.example.com/app.js"
]
}'
# 全キャッシュのパージ
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache" \
-H "Authorization: Bearer {api_token}" \
-H "Content-Type: application/json" \
--data '{"purge_everything": true}'
9.8 Vary ヘッダーとキャッシュ
Varyヘッダーは、同じURLでもリクエストヘッダーの値によって異なるレスポンスが返される場合に、キャッシュキーの一部としてどのヘッダーを使うべきかを指定する。
# 圧縮方式によってキャッシュを分離
Vary: Accept-Encoding
# 言語によってキャッシュを分離
Vary: Accept-Language
# 複数のヘッダーでキャッシュを分離
Vary: Accept-Encoding, Accept-Language, User-Agent
注意: Vary: User-Agent のように値のバリエーションが多すぎるヘッダーを指定すると、キャッシュヒット率が極端に低下する。CDNでの使用には注意が必要である。
第10章: 認証と認可
10.1 認証と認可の違い
- 認証(Authentication): 「あなたは誰か?」— ユーザーのアイデンティティを確認するプロセス
- 認可(Authorization): 「あなたは何ができるか?」— 認証されたユーザーのアクセス権限を制御するプロセス
認証フロー:
ユーザー → [ログイン情報を提示] → サーバーが身元を確認 → トークン/セッション発行
認可フロー:
ユーザー → [トークンを提示してリソースにアクセス] → サーバーが権限を確認 → 許可/拒否
10.2 Basic認証
Basic認証は、HTTPの最も基本的な認証方式である。ユーザー名とパスワードをBase64エンコードしてAuthorizationヘッダーで送信する。
# curl での Basic 認証
curl -u username:password https://api.example.com/protected
# Base64 エンコードを手動で行う場合
echo -n "username:password" | base64
# dXNlcm5hbWU6cGFzc3dvcmQ=
curl -H "Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=" https://api.example.com/protected
# 認証が必要な場合のサーバーレスポンス
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Restricted Area"
# 認証成功
HTTP/1.1 200 OK
Content-Type: application/json
# Nginx での Basic 認証設定
location /admin/ {
auth_basic "Admin Area";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://backend;
}
# パスワードファイルの作成
# htpasswd のインストール (Ubuntu)
sudo apt install apache2-utils
# ユーザーの追加
sudo htpasswd -c /etc/nginx/.htpasswd admin
# パスワードの入力を求められる
Basic認証の問題点: Base64は暗号化ではなくエンコーディングであるため、HTTPS無しでは認証情報が平文で送信される。必ずHTTPSと組み合わせて使用すること。
10.3 Digest認証
Digest認証は、Basic認証のセキュリティ上の弱点を改善した認証方式である。パスワードのハッシュ値を使用するため、パスワードが平文で送信されることはない。
# サーバーからのチャレンジ
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest realm="example@example.com",
qop="auth",
nonce="dcd98b...",
opaque="5ccc06..."
# クライアントからのレスポンス
GET /protected HTTP/1.1
Authorization: Digest username="admin",
realm="example@example.com",
nonce="dcd98b...",
uri="/protected",
qop=auth,
nc=00000001,
cnonce="0a4f113b",
response="6629fae4...",
opaque="5ccc06..."
現在では、TLS(HTTPS)の普及により、Digest認証の利点は薄れており、Bearer Token認証やOAuth 2.0が推奨されている。
10.4 Bearer Token / OAuth 2.0
OAuth 2.0は、サードパーティアプリケーションにユーザーのリソースへのアクセスを安全に委譲するためのフレームワークである。
OAuth 2.0 の認可コードフロー
1. ユーザーが認可サーバーにリダイレクトされる
GET https://auth.example.com/authorize?
response_type=code&
client_id=YOUR_CLIENT_ID&
redirect_uri=https://app.example.com/callback&
scope=read write&
state=xyz123
2. ユーザーが認可を承認 → コールバックURLにリダイレクト
GET https://app.example.com/callback?
code=AUTH_CODE_HERE&
state=xyz123
3. アプリケーションが認可コードをアクセストークンに交換
POST https://auth.example.com/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=AUTH_CODE_HERE&
redirect_uri=https://app.example.com/callback&
client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET
レスポンス:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "dGhpcyBpcyBhIHJlZnJlc2gg...",
"scope": "read write"
}
4. アクセストークンを使ってAPIにアクセス
GET /api/user/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
OAuth 2.0 のクライアントクレデンシャルフロー(M2M通信)
# マシン間通信用のトークン取得
curl -X POST https://auth.example.com/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=service-a" \
-d "client_secret=secret123" \
-d "scope=api.read"
10.5 JWT(JSON Web Token)
JWTは、JSON形式のトークンで、認証情報やクレーム(権限情報等)を安全にやり取りするための標準(RFC 7519)である。
JWT の構造:
ヘッダー.ペイロード.署名
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzEyODM0NDAwLCJleHAiOjE3MTI4MzgwMDB9.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
// デコードされたヘッダー
{
"alg": "HS256",
"typ": "JWT"
}
// デコードされたペイロード
{
"sub": "1234567890",
"name": "Alice",
"role": "admin",
"iat": 1712834400,
"exp": 1712838000
}
# JWT を使ったAPIアクセス
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
https://api.example.com/protected/resource
JWTのベストプラクティス:
- アルゴリズムにはRS256(RSA + SHA-256)またはES256(ECDSA + SHA-256)を推奨
- HS256(対称鍵)はサービス間通信でのみ使用
- 有効期限(exp)を短く設定する(15分〜1時間)
- リフレッシュトークンと組み合わせて使用する
- JWTにはパスワードや個人情報を含めない
10.6 Cookie-based 認証とセッション管理
# サーバーサイドセッション方式
1. ログインリクエスト
POST /login HTTP/1.1
Content-Type: application/json
{"username": "alice", "password": "secret"}
2. セッション作成とCookie設定
HTTP/1.1 200 OK
Set-Cookie: session_id=a1b2c3d4e5; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
3. 後続のリクエスト
GET /dashboard HTTP/1.1
Cookie: session_id=a1b2c3d4e5
セキュアなCookie設定
| 属性 | 説明 | 推奨値 |
|---|---|---|
| HttpOnly | JavaScriptからのアクセスを禁止(XSS対策) | 必須 |
| Secure | HTTPS接続でのみ送信 | 必須 |
| SameSite | クロスサイトリクエストでの送信制御(CSRF対策) | Strict または Lax |
| Path | Cookieが送信されるパス | / |
| Max-Age / Expires | 有効期限 | 用途に応じて設定 |
| Domain | Cookieが送信されるドメイン | 明示的に設定 |
10.7 API Key認証
API Keyは、APIへのアクセスを制御するための最もシンプルな認証方式である。
# ヘッダーによるAPI Key
curl -H "X-API-Key: your-api-key-here" \
https://api.example.com/data
# クエリパラメータによるAPI Key(非推奨: URLログに記録される)
curl "https://api.example.com/data?api_key=your-api-key-here"
# Authorizationヘッダー
curl -H "Authorization: ApiKey your-api-key-here" \
https://api.example.com/data
# Nginx での API Key 検証
map $http_x_api_key $api_key_valid {
default 0;
"sk_live_abc123" 1;
"sk_live_def456" 1;
}
server {
location /api/ {
if ($api_key_valid = 0) {
return 401 '{"error": "Invalid API key"}';
}
proxy_pass http://backend;
}
}
10.8 認証方式の比較と使い分け
| 方式 | ユースケース | 複雑さ | セキュリティ |
|---|---|---|---|
| Basic認証 | 内部ツール、CI/CD | 低 | 低(HTTPS必須) |
| API Key | 公開API、M2M | 低 | 中 |
| Cookie/Session | Webアプリケーション | 中 | 中〜高 |
| OAuth 2.0 | サードパーティ連携 | 高 | 高 |
| JWT | SPA、マイクロサービス | 中 | 中〜高 |
| mTLS | マイクロサービス間通信 | 高 | 非常に高 |
第11章: REST APIの設計とHTTP
11.1 RESTアーキテクチャスタイルの原則
REST(Representational State Transfer)は、2000年にRoy Fieldingが博士論文で提唱したアーキテクチャスタイルであり、以下の6つの制約を定義している。
- クライアント・サーバー分離: UIとデータストレージの関心事を分離
- ステートレス: 各リクエストはそれ自体で完結し、サーバーにセッション状態を保持しない
- キャッシュ可能: レスポンスをキャッシュ可能として明示的にマークできる
- 統一インターフェース: リソースの識別、表現によるリソース操作、自己記述的メッセージ、HATEOAS
- 階層化システム: クライアントはエンドサーバーに直接接続しているか中間者を経由しているか認識しない
- コードオンデマンド(オプション): サーバーがクライアントに実行可能コードを送信できる
11.2 RESTful API設計のベストプラクティス
URL設計
# 良い例(名詞、複数形、階層構造)
GET /api/v1/users # ユーザー一覧
GET /api/v1/users/123 # ユーザー詳細
POST /api/v1/users # ユーザー作成
PUT /api/v1/users/123 # ユーザー更新(全体)
PATCH /api/v1/users/123 # ユーザー更新(部分)
DELETE /api/v1/users/123 # ユーザー削除
GET /api/v1/users/123/orders # ユーザーの注文一覧
GET /api/v1/users/123/orders/456 # 特定の注文
# 悪い例(動詞、不統一)
GET /api/getUsers # ❌ 動詞を使用
POST /api/createUser # ❌ 動詞を使用
GET /api/v1/user/123 # ❌ 単数形
DELETE /api/v1/users/123/delete # ❌ URLに動詞
HTTPメソッドとCRUD操作のマッピング
| CRUD操作 | HTTPメソッド | URL例 | ステータスコード |
|---|---|---|---|
| Create | POST | POST /users | 201 Created |
| Read (一覧) | GET | GET /users | 200 OK |
| Read (詳細) | GET | GET /users/123 | 200 OK |
| Update (全体) | PUT | PUT /users/123 | 200 OK |
| Update (部分) | PATCH | PATCH /users/123 | 200 OK |
| Delete | DELETE | DELETE /users/123 | 204 No Content |
11.3 ステータスコードの適切な使用
# 作成成功 — 201 Created(Locationヘッダーで新リソースのURLを返す)
curl -X POST https://api.example.com/v1/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "alice@example.com"}'
# HTTP/1.1 201 Created
# Location: /api/v1/users/124
# Content-Type: application/json
# {"id": 124, "name": "Alice", "email": "alice@example.com"}
# バリデーションエラー — 422 Unprocessable Content
# HTTP/1.1 422 Unprocessable Content
# {
# "errors": [
# {"field": "email", "message": "Invalid email format"},
# {"field": "name", "message": "Name is required"}
# ]
# }
# リソースの競合 — 409 Conflict
# HTTP/1.1 409 Conflict
# {"error": "User with this email already exists"}
11.4 ページネーション
# オフセットベースのページネーション
GET /api/v1/users?page=2&per_page=20
# レスポンス
HTTP/1.1 200 OK
X-Total-Count: 150
Link: </api/v1/users?page=1&per_page=20>; rel="prev",
</api/v1/users?page=3&per_page=20>; rel="next",
</api/v1/users?page=8&per_page=20>; rel="last",
</api/v1/users?page=1&per_page=20>; rel="first"
{
"data": [...],
"pagination": {
"page": 2,
"per_page": 20,
"total": 150,
"total_pages": 8
}
}
# カーソルベースのページネーション(大規模データセット向け)
GET /api/v1/users?cursor=eyJpZCI6MTIzfQ&limit=20
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTQzfQ",
"has_more": true
}
}
11.5 フィルタリング、ソート、フィールド選択
# フィルタリング
GET /api/v1/users?status=active&role=admin&created_after=2026-01-01
# ソート
GET /api/v1/users?sort=created_at&order=desc
GET /api/v1/users?sort=-created_at,+name # プレフィックス方式
# フィールド選択(Sparse Fieldsets)
GET /api/v1/users?fields=id,name,email
# 検索
GET /api/v1/users?q=alice&search_fields=name,email
# 複合クエリの例
GET /api/v1/users?status=active&role=admin&sort=-created_at&fields=id,name,email&page=1&per_page=20
11.6 バージョニング
# URLパスによるバージョニング(最も一般的)
GET /api/v1/users
GET /api/v2/users
# ヘッダーによるバージョニング
GET /api/users
Accept: application/vnd.example.v2+json
# クエリパラメータによるバージョニング
GET /api/users?version=2
# Content Negotiation によるバージョニング
GET /api/users
Accept: application/json; version=2
11.7 エラーレスポンスの設計
// RFC 7807 — Problem Details for HTTP APIs
{
"type": "https://api.example.com/errors/validation-error",
"title": "Validation Error",
"status": 422,
"detail": "The request body contains invalid fields.",
"instance": "/api/v1/users",
"errors": [
{
"field": "email",
"message": "Invalid email format",
"code": "INVALID_FORMAT"
},
{
"field": "age",
"message": "Must be a positive integer",
"code": "INVALID_VALUE"
}
],
"timestamp": "2026-04-11T12:00:00Z",
"trace_id": "abc-123-def-456"
}
11.8 HATEOAS(Hypermedia as the Engine of Application State)
HATEOASは、REST成熟度モデルの最高レベルであり、レスポンスにリンク情報を含めることで、クライアントがAPIの構造を動的に発見できるようにする。
{
"id": 123,
"name": "Alice",
"email": "alice@example.com",
"status": "active",
"_links": {
"self": {"href": "/api/v1/users/123"},
"orders": {"href": "/api/v1/users/123/orders"},
"deactivate": {
"href": "/api/v1/users/123/deactivate",
"method": "POST"
},
"update": {
"href": "/api/v1/users/123",
"method": "PUT"
}
}
}
11.9 OpenAPI / Swagger 仕様
OpenAPI仕様(旧Swagger)は、RESTful APIを記述するための業界標準フォーマットである。
# openapi.yaml — OpenAPI 3.0 仕様の例
openapi: 3.0.3
info:
title: User Management API
description: ユーザー管理APIの仕様書
version: 1.0.0
contact:
name: API Support
email: api-support@example.com
servers:
- url: https://api.example.com/v1
description: Production
- url: https://staging-api.example.com/v1
description: Staging
paths:
/users:
get:
summary: ユーザー一覧の取得
operationId: listUsers
tags:
- Users
parameters:
- name: page
in: query
schema:
type: integer
default: 1
- name: per_page
in: query
schema:
type: integer
default: 20
maximum: 100
- name: status
in: query
schema:
type: string
enum: [active, inactive, suspended]
responses:
'200':
description: 成功
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
pagination:
$ref: '#/components/schemas/Pagination'
'401':
$ref: '#/components/responses/Unauthorized'
security:
- bearerAuth: []
post:
summary: ユーザーの作成
operationId: createUser
tags:
- Users
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
responses:
'201':
description: 作成成功
headers:
Location:
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'422':
$ref: '#/components/responses/ValidationError'
/users/{userId}:
get:
summary: ユーザー詳細の取得
operationId: getUser
tags:
- Users
parameters:
- name: userId
in: path
required: true
schema:
type: integer
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
$ref: '#/components/responses/NotFound'
components:
schemas:
User:
type: object
properties:
id:
type: integer
name:
type: string
email:
type: string
format: email
status:
type: string
enum: [active, inactive, suspended]
created_at:
type: string
format: date-time
required:
- id
- name
- email
CreateUserRequest:
type: object
properties:
name:
type: string
minLength: 1
maxLength: 100
email:
type: string
format: email
required:
- name
- email
Pagination:
type: object
properties:
page:
type: integer
per_page:
type: integer
total:
type: integer
total_pages:
type: integer
responses:
Unauthorized:
description: 認証エラー
content:
application/json:
schema:
type: object
properties:
error:
type: string
NotFound:
description: リソースが見つからない
ValidationError:
description: バリデーションエラー
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
第12章: WebSocket、SSE、gRPC
12.1 HTTPの限界とリアルタイム通信
HTTPのリクエスト・レスポンスモデルは、クライアントが常にリクエストを発信する「プル型」通信であり、サーバーからクライアントへの即時的なデータ送信(プッシュ型通信)には適していない。チャット、株価ティッカー、リアルタイム通知などのユースケースでは、以下のようなリアルタイム通信技術が使用される。
12.2 WebSocket プロトコル
WebSocket(RFC 6455)は、HTTP接続をアップグレードして、全二重(Full-Duplex)通信チャネルを確立するプロトコルである。
WebSocket ハンドシェイク
# クライアントからのアップグレードリクエスト
GET /chat HTTP/1.1
Host: ws.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Sec-WebSocket-Protocol: chat, superchat
Origin: https://www.example.com
# サーバーからのアップグレード応答
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
# 以降、WebSocketフレームによる双方向通信
JavaScript での WebSocket 実装例
// クライアント側
const ws = new WebSocket('wss://ws.example.com/chat');
ws.onopen = () => {
console.log('Connected');
ws.send(JSON.stringify({
type: 'message',
content: 'Hello, World!'
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received:', data);
};
ws.onclose = (event) => {
console.log(`Disconnected: ${event.code} ${event.reason}`);
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
# サーバー側(Python FastAPI + websockets)
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from typing import List
app = FastAPI()
class ConnectionManager:
def __init__(self):
self.active_connections: List[WebSocket] = []
async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket)
def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket)
async def broadcast(self, message: str):
for connection in self.active_connections:
await connection.send_text(message)
manager = ConnectionManager()
@app.websocket("/ws/chat")
async def websocket_endpoint(websocket: WebSocket):
await manager.connect(websocket)
try:
while True:
data = await websocket.receive_text()
await manager.broadcast(f"Message: {data}")
except WebSocketDisconnect:
manager.disconnect(websocket)
# Nginx での WebSocket プロキシ設定
upstream websocket_backend {
server 127.0.0.1:8080;
}
server {
listen 443 ssl;
server_name ws.example.com;
location /ws/ {
proxy_pass http://websocket_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# WebSocket のタイムアウト
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
12.3 Server-Sent Events(SSE)
SSE(Server-Sent Events)は、サーバーからクライアントへの単方向ストリーミングを実現するHTTPベースの技術である。通常のHTTPレスポンスとしてイベントストリームを送信するため、特別なプロトコルは不要である。
# SSE のレスポンス
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
data: {"message": "Hello"}
data: {"message": "World"}
event: notification
data: {"type": "alert", "content": "Server maintenance at 3:00 AM"}
id: 12345
event: update
data: {"price": 150.25, "symbol": "AAPL"}
retry: 5000
SSE のフォーマット
data:— イベントのデータ(複数行可)event:— イベントの種類(カスタムイベント名)id:— イベントID(再接続時にLast-Event-IDヘッダーで送信される)retry:— 再接続間隔(ミリ秒)
// クライアント側
const eventSource = new EventSource('https://api.example.com/events');
// デフォルトの message イベント
eventSource.onmessage = (event) => {
console.log('Data:', JSON.parse(event.data));
};
// カスタムイベント
eventSource.addEventListener('notification', (event) => {
console.log('Notification:', JSON.parse(event.data));
});
eventSource.addEventListener('update', (event) => {
console.log('Update:', JSON.parse(event.data));
});
eventSource.onerror = (error) => {
console.error('SSE Error:', error);
};
# サーバー側(Python FastAPI)
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import asyncio
import json
app = FastAPI()
async def event_generator():
count = 0
while True:
count += 1
data = json.dumps({"count": count, "timestamp": "2026-04-11T12:00:00Z"})
yield f"id: {count}\nevent: update\ndata: {data}\n\n"
await asyncio.sleep(1)
@app.get("/events")
async def sse_endpoint():
return StreamingResponse(
event_generator(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no", # Nginx でのバッファリング無効化
}
)
12.4 gRPC over HTTP/2
gRPCは、Googleが開発した高性能なRPCフレームワークであり、HTTP/2をトランスポートプロトコルとして使用する。Protocol Buffersをインターフェース定義言語(IDL)およびシリアライゼーション形式として使用する。
// user_service.proto — Protocol Buffers定義
syntax = "proto3";
package user;
service UserService {
// Unary RPC(通常のリクエスト・レスポンス)
rpc GetUser (GetUserRequest) returns (User);
rpc CreateUser (CreateUserRequest) returns (User);
// Server Streaming RPC
rpc ListUsers (ListUsersRequest) returns (stream User);
// Client Streaming RPC
rpc UploadUsers (stream User) returns (UploadUsersResponse);
// Bidirectional Streaming RPC
rpc Chat (stream ChatMessage) returns (stream ChatMessage);
}
message User {
int32 id = 1;
string name = 2;
string email = 3;
}
message GetUserRequest {
int32 id = 1;
}
message CreateUserRequest {
string name = 1;
string email = 2;
}
message ListUsersRequest {
int32 page_size = 1;
string page_token = 2;
}
message UploadUsersResponse {
int32 count = 1;
}
message ChatMessage {
string user = 1;
string content = 2;
int64 timestamp = 3;
}
# gRPC サーバー(Python)
import grpc
from concurrent import futures
import user_service_pb2
import user_service_pb2_grpc
class UserServicer(user_service_pb2_grpc.UserServiceServicer):
def GetUser(self, request, context):
return user_service_pb2.User(
id=request.id,
name="Alice",
email="alice@example.com"
)
def ListUsers(self, request, context):
for i in range(10):
yield user_service_pb2.User(
id=i, name=f"User {i}", email=f"user{i}@example.com"
)
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
user_service_pb2_grpc.add_UserServiceServicer_to_server(UserServicer(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
serve()
# Nginx での gRPC リバースプロキシ設定
upstream grpc_backend {
server 127.0.0.1:50051;
}
server {
listen 443 ssl http2;
server_name grpc.example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
location / {
grpc_pass grpc://grpc_backend;
# gRPC ヘルスチェック
grpc_read_timeout 300s;
grpc_send_timeout 300s;
# エラーハンドリング
error_page 502 = /error502grpc;
}
location = /error502grpc {
internal;
default_type application/grpc;
add_header grpc-status 14;
add_header grpc-message "unavailable";
return 204;
}
}
12.5 各プロトコルの比較と使い分け
| 特性 | HTTP (REST) | WebSocket | SSE | gRPC |
|---|---|---|---|---|
| 通信方向 | リクエスト・レスポンス | 双方向 | サーバー→クライアント | 双方向 |
| プロトコル | HTTP/1.1, HTTP/2 | WS/WSS | HTTP/1.1, HTTP/2 | HTTP/2 |
| データ形式 | JSON, XML等 | 任意 | テキスト | Protocol Buffers |
| ブラウザサポート | 完全 | 完全 | 完全(IE除く) | grpc-web経由 |
| 再接続 | N/A | 手動実装 | 自動 | 手動実装 |
| 用途 | CRUD API | チャット、ゲーム | 通知、ダッシュボード | マイクロサービス |
| パフォーマンス | 中 | 高 | 中〜高 | 非常に高 |
| 実装の複雑さ | 低 | 中 | 低 | 高 |
使い分けの指針
- REST API: CRUD操作、パブリックAPI、ブラウザからのデータ取得
- WebSocket: リアルタイム双方向通信(チャット、オンラインゲーム、コラボレーションツール)
- SSE: サーバーからの一方的なイベント配信(通知、ダッシュボード更新、ログストリーミング)
- gRPC: マイクロサービス間の高性能通信、ストリーミングが必要な内部サービス
第13章: HTTPの監視・デバッグ・トラブルシューティング
13.1 ブラウザDevToolsによるHTTP分析
Chrome DevTools(または同等のブラウザ開発者ツール)は、HTTPのデバッグにおいて最も手軽で強力なツールである。
Network タブの活用
DevTools → Network タブ で確認できる情報:
- リクエスト/レスポンスのヘッダー
- ステータスコード
- リクエスト/レスポンスのボディ
- タイミング情報(DNS、TCP、TLS、TTFB、コンテンツダウンロード)
- ウォーターフォール図(リクエストの依存関係と並列度)
- プロトコルバージョン(h2、h3)
- Content-Encoding(圧縮方式)
- キャッシュの使用状況(disk cache、memory cache、304)
Performance タブでのタイミング分析
主要なパフォーマンスメトリクス:
- TTFB (Time to First Byte): リクエスト送信からレスポンスの最初のバイトを受信するまでの時間
- FCP (First Contentful Paint): 最初のコンテンツが描画されるまでの時間
- LCP (Largest Contentful Paint): 最大のコンテンツが描画されるまでの時間
- CLS (Cumulative Layout Shift): 視覚的安定性の指標
13.2 curl によるデバッグ
# 詳細な通信情報を表示(-v / --verbose)
curl -v https://www.example.com 2>&1
# タイミング情報の詳細表示
curl -w @- -o /dev/null -s https://www.example.com <<'EOF'
DNS Lookup: %{time_namelookup}s
TCP Connect: %{time_connect}s
TLS Handshake: %{time_appconnect}s
Start Transfer:%{time_starttransfer}s (TTFB)
Total Time: %{time_total}s
Download Size: %{size_download} bytes
Upload Size: %{size_upload} bytes
HTTP Code: %{http_code}
HTTP Version: %{http_version}
Remote IP: %{remote_ip}
SSL Verify: %{ssl_verify_result}
Redirect URL: %{redirect_url}
Num Redirects: %{num_redirects}
EOF
# リクエスト/レスポンスヘッダーのみを表示
curl -sI https://www.example.com
# リクエストとレスポンスの全トレースをファイルに保存
curl --trace trace.log https://www.example.com
curl --trace-ascii trace.txt https://www.example.com
# 特定のHTTPバージョンを強制
curl --http1.1 -v https://www.example.com
curl --http2 -v https://www.example.com
curl --http3 -v https://www.example.com
# DNS解決時間の問題を診断
curl --resolve www.example.com:443:93.184.216.34 https://www.example.com
# 接続の再利用を確認
curl -v https://www.example.com https://www.example.com/page2 2>&1 | grep -E "Re-using|Connected"
13.3 HTTPie によるデバッグ
# HTTPie の基本的な使い方(curl よりも可読性が高い出力)
http GET https://api.example.com/users
# 詳細表示(リクエストヘッダー付き)
http -v GET https://api.example.com/users
# ヘッダーのみ表示
http --headers GET https://api.example.com/users
# リクエストのみ表示(送信せずに確認)
http --print=HB GET https://api.example.com/users Authorization:"Bearer token"
# セッション機能(Cookieの保持)
http --session=mysession POST https://api.example.com/login username=admin password=secret
http --session=mysession GET https://api.example.com/dashboard
13.4 Wireshark / tcpdump によるパケットキャプチャ
# tcpdump でHTTPトラフィックをキャプチャ(非暗号化通信のみ)
sudo tcpdump -i eth0 -A 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'
# 特定のホストへのHTTP/HTTPSトラフィック
sudo tcpdump -i eth0 host www.example.com and port 443 -w capture.pcap
# tcpdump のキャプチャをWiresharkで分析
wireshark capture.pcap &
# tshark(Wiresharkのコマンドラインツール)でHTTP/2フレームを分析
tshark -i eth0 -f "tcp port 443" -Y "http2" -T fields \
-e http2.streamid -e http2.type -e http2.length
# QUIC/HTTP/3トラフィックのキャプチャ
sudo tcpdump -i eth0 'udp port 443' -w quic_capture.pcap
SSLKEYLOGFILE による暗号化トラフィックの復号
# TLS暗号鍵をログファイルに記録
export SSLKEYLOGFILE=~/tls_keys.log
# curl でリクエストを実行
curl https://www.example.com
# Wireshark で復号して分析
# Edit → Preferences → Protocols → TLS → (Pre)-Master-Secret log filename に ~/tls_keys.log を指定
13.5 Nginx のログ設定
# アクセスログの詳細フォーマット
log_format detailed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time '
'uct=$upstream_connect_time '
'uht=$upstream_header_time '
'urt=$upstream_response_time '
'cs=$upstream_cache_status '
'proto=$server_protocol '
'ssl=$ssl_protocol/$ssl_cipher '
'rid=$request_id';
access_log /var/log/nginx/access.log detailed;
# JSON形式のログ(構造化ログ)
log_format json_combined escape=json
'{'
'"time": "$time_iso8601",'
'"remote_addr": "$remote_addr",'
'"request_method": "$request_method",'
'"request_uri": "$request_uri",'
'"status": $status,'
'"body_bytes_sent": $body_bytes_sent,'
'"request_time": $request_time,'
'"upstream_response_time": "$upstream_response_time",'
'"http_user_agent": "$http_user_agent",'
'"http_referer": "$http_referer",'
'"request_id": "$request_id",'
'"server_protocol": "$server_protocol",'
'"ssl_protocol": "$ssl_protocol",'
'"upstream_cache_status": "$upstream_cache_status"'
'}';
access_log /var/log/nginx/access.json json_combined;
# エラーログのレベル設定
error_log /var/log/nginx/error.log warn;
# 条件付きログ(特定のステータスコードのみ記録)
map $status $loggable {
~^[23] 0;
default 1;
}
access_log /var/log/nginx/error_access.log detailed if=$loggable;
13.6 Apache のログ設定
# カスタムログフォーマット
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D %{X-Request-ID}i" combined_plus
CustomLog /var/log/apache2/access.log combined_plus
# SSL情報を含むログ
LogFormat "%h %l %u %t \"%r\" %>s %b %{SSL_PROTOCOL}x %{SSL_CIPHER}x" ssl_combined
# 条件付きログ(画像ファイルを除外)
SetEnvIf Request_URI "\.(gif|jpg|png|ico|css|js)$" no_log
CustomLog /var/log/apache2/access.log combined env=!no_log
13.7 パフォーマンス最適化のポイント
HTTPパフォーマンス最適化チェックリスト:
□ 接続の最適化
├── HTTP/2 または HTTP/3 の有効化
├── Keep-Alive の適切な設定
├── DNS プリフェッチの活用
├── TCP Fast Open の有効化
└── TLS 1.3 の使用
□ 転送量の削減
├── Gzip / Brotli 圧縮の有効化
├── 画像の最適化(WebP、AVIF形式の使用)
├── 不要なヘッダーの削除
├── レスポンスボディの最小化
└── Tree Shaking(未使用コードの削除)
□ キャッシュの活用
├── 適切な Cache-Control の設定
├── ETag / Last-Modified の活用
├── CDN の導入
├── ブラウザキャッシュの最大活用
└── stale-while-revalidate の活用
□ リクエスト数の削減
├── CSS / JS のバンドル
├── CSS Sprites / SVG の活用
├── データURIの使用(小さなリソース)
├── HTTP/2 では過度なバンドルは不要
└── Prefetch / Preload の活用
□ サーバー側の最適化
├── TTFB の削減
├── データベースクエリの最適化
├── アプリケーションキャッシュ(Redis等)
├── Connection Pooling
└── 適切なタイムアウトの設定
# パフォーマンステスト用ツール
# Apache Bench (ab) での負荷テスト
ab -n 1000 -c 100 https://www.example.com/
# wrk での負荷テスト
wrk -t12 -c400 -d30s https://www.example.com/
# k6 での負荷テスト
k6 run --vus 100 --duration 30s script.js
# lighthouse によるパフォーマンス監査
lighthouse https://www.example.com --output json --output-path report.json
第14章: ロードバランサーとリバースプロキシ
14.1 ロードバランシングの概要
ロードバランシングは、受信したHTTPリクエストを複数のバックエンドサーバーに分散させることで、可用性、スケーラビリティ、パフォーマンスを向上させる技術である。
┌── Server 1 (10.0.0.1)
クライアント → ロードバランサー ─┼── Server 2 (10.0.0.2)
├── Server 3 (10.0.0.3)
└── Server 4 (10.0.0.4)
14.2 L4(トランスポート層) vs L7(アプリケーション層)ロードバランシング
| 特性 | L4 ロードバランサー | L7 ロードバランサー |
|---|---|---|
| 動作層 | TCP/UDP レベル | HTTP レベル |
| 判断基準 | IPアドレス、ポート番号 | URL、ヘッダー、Cookie、ボディ |
| SSL終端 | 不可(パススルー) | 可能 |
| コンテンツルーティング | 不可 | 可能 |
| パフォーマンス | 非常に高い | 高い(処理は多い) |
| WebSocket | パススルー | 対応可能 |
| 例 | iptables, LVS, NLB | Nginx, HAProxy, ALB |
14.3 ロードバランシングのアルゴリズム
1. ラウンドロビン(Round Robin):
リクエスト1 → Server 1
リクエスト2 → Server 2
リクエスト3 → Server 3
リクエスト4 → Server 1 ← 最初に戻る
...
2. 重み付きラウンドロビン(Weighted Round Robin):
Server 1 (weight=3): リクエスト1, 2, 3
Server 2 (weight=1): リクエスト4
Server 3 (weight=2): リクエスト5, 6
...
3. 最小接続数(Least Connections):
→ 現在の接続数が最も少ないサーバーにリクエストを送信
4. IPハッシュ(IP Hash):
→ クライアントのIPアドレスのハッシュ値に基づいてサーバーを選択
→ 同じクライアントは常に同じサーバーに接続
5. 最小応答時間(Least Time):
→ 応答時間が最も短いサーバーにリクエストを送信
14.4 Nginx ロードバランサー設定例
# /etc/nginx/conf.d/load-balancer.conf
# アップストリームサーバーの定義
upstream api_backend {
# ロードバランシングアルゴリズム
# デフォルト: ラウンドロビン
# least_conn; # 最小接続数
# ip_hash; # IPハッシュ
# hash $request_uri; # URIハッシュ(キャッシュに有効)
# サーバーの定義(重み付き)
server 10.0.0.1:8080 weight=3;
server 10.0.0.2:8080 weight=2;
server 10.0.0.3:8080 weight=1;
# バックアップサーバー(他のサーバーが全てダウンした場合のみ使用)
server 10.0.0.4:8080 backup;
# ダウンとみなすサーバー(メンテナンス中)
# server 10.0.0.5:8080 down;
# Keep-Alive 接続の維持
keepalive 32;
keepalive_timeout 60s;
keepalive_requests 1000;
# ヘルスチェック(Nginx Plus のみ)
# health_check interval=5s fails=3 passes=2;
}
upstream static_backend {
server 10.0.1.1:80;
server 10.0.1.2:80;
}
server {
listen 443 ssl;
http2 on;
server_name api.example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# API リクエストのルーティング
location /api/ {
proxy_pass http://api_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Request-ID $request_id;
# タイムアウト設定
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
# バッファリング設定
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 8k;
# エラー時の次サーバーへのフェイルオーバー
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
}
# 静的ファイルは別のバックエンドへ
location /static/ {
proxy_pass http://static_backend;
proxy_cache_valid 200 1d;
}
# ヘルスチェックエンドポイント
location /health {
access_log off;
return 200 '{"status": "healthy"}';
add_header Content-Type application/json;
}
}
14.5 Nginx でのパッシブヘルスチェック
upstream backend {
server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.3:8080 max_fails=3 fail_timeout=30s;
}
# max_fails: この回数失敗するとサーバーを一時的にダウンとみなす
# fail_timeout: ダウンとみなす期間、およびこの期間内の失敗をカウント
14.6 HAProxy 設定例
# /etc/haproxy/haproxy.cfg
global
log /dev/log local0
log /dev/log local1 notice
maxconn 50000
user haproxy
group haproxy
daemon
# SSL/TLS 設定
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
tune.ssl.default-dh-param 2048
defaults
log global
mode http
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
retries 3
timeout connect 5s
timeout client 30s
timeout server 30s
timeout http-request 10s
timeout http-keep-alive 10s
timeout queue 30s
timeout check 5s
# エラーページ
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
# フロントエンド(受信側)
frontend http_front
bind *:80
# HTTPSへのリダイレクト
redirect scheme https code 301 if !{ ssl_fc }
frontend https_front
bind *:443 ssl crt /etc/haproxy/ssl/cert.pem alpn h2,http/1.1
mode http
# リクエストヘッダーの追加
http-request set-header X-Forwarded-Proto https
http-request set-header X-Request-ID %[uuid()]
# コンテンツベースのルーティング
acl is_api path_beg /api/
acl is_static path_beg /static/
acl is_websocket hdr(Upgrade) -i websocket
use_backend api_servers if is_api
use_backend static_servers if is_static
use_backend websocket_servers if is_websocket
default_backend web_servers
# バックエンド(送信先)
backend api_servers
balance leastconn
option httpchk GET /health
http-check expect status 200
# レートリミット
stick-table type ip size 100k expire 30s store http_req_rate(10s)
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }
http-request track-sc0 src
server api1 10.0.0.1:8080 check inter 5s fall 3 rise 2 weight 3
server api2 10.0.0.2:8080 check inter 5s fall 3 rise 2 weight 2
server api3 10.0.0.3:8080 check inter 5s fall 3 rise 2 weight 1
server api4 10.0.0.4:8080 check inter 5s fall 3 rise 2 backup
backend web_servers
balance roundrobin
option httpchk GET /
http-check expect status 200
cookie SERVERID insert indirect nocache
server web1 10.0.1.1:80 check cookie web1
server web2 10.0.1.2:80 check cookie web2
backend static_servers
balance roundrobin
server static1 10.0.2.1:80 check
server static2 10.0.2.2:80 check
backend websocket_servers
balance source
option http-server-close
timeout tunnel 3600s
server ws1 10.0.3.1:8080 check
server ws2 10.0.3.2:8080 check
# 統計ダッシュボード
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 10s
stats admin if LOCALHOST
stats auth admin:password
14.7 セッションアフィニティ(スティッキーセッション)
セッションアフィニティは、同じクライアントからのリクエストを常に同じバックエンドサーバーに送信する機能である。
# Nginx — IP Hash によるセッションアフィニティ
upstream backend {
ip_hash;
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}
# Nginx — Cookie によるセッションアフィニティ(Nginx Plus)
upstream backend {
sticky cookie srv_id expires=1h domain=.example.com path=/;
server 10.0.0.1:8080;
server 10.0.0.2:8080;
}
注意: セッションアフィニティはスケーラビリティを制限するため、可能であればステートレスな設計(JWT、外部セッションストアの使用)が推奨される。
14.8 SSL終端(SSL Termination)
SSL終端は、ロードバランサー(リバースプロキシ)でTLS接続を終了させ、バックエンドサーバーへは平文(HTTP)で通信する構成である。
クライアント ──[HTTPS]──→ ロードバランサー ──[HTTP]──→ バックエンドサーバー
(SSL終端)
利点:
- バックエンドサーバーのCPU負荷を軽減
- 証明書管理の一元化
- HTTP/2の一元的な処理
# Nginx — SSL 終端の設定
server {
listen 443 ssl;
http2 on;
server_name www.example.com;
# SSL/TLS 設定
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
location / {
# バックエンドへはHTTPで接続
proxy_pass http://backend;
proxy_set_header X-Forwarded-Proto https; # 元のプロトコルを通知
}
}
14.9 ブルーグリーンデプロイメントとカナリアリリース
# Nginx — カナリアリリースの設定
upstream production {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
}
upstream canary {
server 10.0.1.1:8080;
}
# 全トラフィックの10%をカナリアに送信
split_clients "${remote_addr}" $upstream_variant {
10% canary;
* production;
}
server {
listen 443 ssl;
location / {
proxy_pass http://$upstream_variant;
}
}
# HAProxy — 重み付きによるカナリアリリース
backend app_servers
balance roundrobin
server prod1 10.0.0.1:8080 weight 45
server prod2 10.0.0.2:8080 weight 45
server canary 10.0.1.1:8080 weight 10
第15章: まとめと参考資料
15.1 HTTP技術のまとめ
本記事では、HTTPプロトコルについて以下の15の主要トピックを包括的に解説した。
プロトコルの基礎
- HTTPはクライアント・サーバーモデルに基づくステートレスなアプリケーション層プロトコルである
- リクエストライン(メソッド、URI、バージョン)、ヘッダー、ボディからなるメッセージ構造を持つ
- 9つのHTTPメソッドにはそれぞれ安全性、冪等性、キャッシュ可能性の属性がある
- ステータスコード(1xx〜5xx)により、リクエストの処理結果が体系的に分類される
プロトコルの進化
- HTTP/1.1: Keep-Alive、チャンク転送、条件付きリクエスト、コンテントネゴシエーションにより、HTTP/1.0の課題を解決した
- HTTP/2: バイナリフレーミング、ストリーム多重化、HPACK圧縮により、パフォーマンスを大幅に向上させた
- HTTP/3: QUICプロトコルの採用により、TCP Head-of-Line Blockingの解消、0-RTT接続確立、コネクションマイグレーションを実現した
セキュリティと運用
- HTTPS/TLS: 通信の機密性、完全性、認証を保証する。TLS 1.3で大幅な高速化と安全性向上が実現された
- セキュリティヘッダー: HSTS、CSP、CORS、X-Content-Type-Options等の適切な設定が不可欠である
- 認証と認可: Basic認証、OAuth 2.0、JWT、Cookie/Sessionなど、用途に応じた認証方式を選択する
- キャッシュ制御: Cache-Control、ETag、CDNの適切な組み合わせにより、パフォーマンスとUXを最適化する
API設計と高度なプロトコル
- REST API: HTTPメソッド、ステータスコード、URI設計のベストプラクティスに従い、直感的で一貫性のあるAPIを設計する
- WebSocket/SSE/gRPC: ユースケースに応じてHTTPベースの拡張プロトコルを使い分ける
- ロードバランサー: L4/L7ロードバランシング、ヘルスチェック、セッションアフィニティ、SSL終端を適切に構成する
15.2 今後の動向
HTTP/3の普及
2026年現在、HTTP/3は主要なブラウザ(Chrome、Firefox、Safari、Edge)および主要なCDN(Cloudflare、Akamai、Fastly)でサポートされている。今後さらに普及が進み、HTTP/2を凌ぐ主流プロトコルになると予想される。ただし、UDPトラフィックのファイアウォール通過性やサーバー側の対応状況により、HTTP/2との共存は当面続く見込みである。
WebTransport
WebTransportは、HTTP/3上に構築された新しいプロトコルであり、WebSocketの後継として位置づけられている。以下の特徴を持つ。
- HTTP/3(QUIC)ベースの低レイテンシ通信
- 信頼性のある(reliable)ストリームと、信頼性のない(unreliable)データグラムの両方をサポート
- 多重化されたストリーム
- ゲーム、ライブストリーミング、IoTなどでの活用が期待される
// WebTransport API の使用例(概念的なコード)
const transport = new WebTransport('https://example.com/webtransport');
await transport.ready;
// 信頼性のあるストリーム
const stream = await transport.createBidirectionalStream();
const writer = stream.writable.getWriter();
await writer.write(new TextEncoder().encode('Hello'));
// 信頼性のないデータグラム
const writer_dg = transport.datagrams.writable.getWriter();
await writer_dg.write(new Uint8Array([1, 2, 3, 4]));
その他の注目技術
- Structured Headers(RFC 8941): ヘッダー値のパースを標準化する新しい仕組み
- Client Hints: サーバーがクライアントの情報(デバイスのピクセル比、ネットワーク速度等)を取得するための仕組み
- 103 Early Hints: TLSハンドシェイク中にプリロードヒントを送信し、ページの読み込みを高速化
- Signed HTTP Exchanges(SXG): コンテンツの配信元を暗号的に証明する技術
- Proxy-Status(RFC 9209): プロキシチェーンにおけるエラー原因の特定を容易にする新しいヘッダー
15.3 参考文献とRFCリスト
主要なRFC
| RFC | タイトル | 概要 |
|---|---|---|
| RFC 9110 | HTTP Semantics | HTTPの意味論(メソッド、ステータスコード、ヘッダー) |
| RFC 9111 | HTTP Caching | HTTPキャッシュ |
| RFC 9112 | HTTP/1.1 | HTTP/1.1メッセージ構文 |
| RFC 9113 | HTTP/2 | HTTP/2プロトコル |
| RFC 9114 | HTTP/3 | HTTP/3プロトコル |
| RFC 9000 | QUIC | QUICトランスポートプロトコル |
| RFC 9001 | Using TLS to Secure QUIC | QUICのTLS統合 |
| RFC 9204 | QPACK | HTTP/3用ヘッダー圧縮 |
| RFC 7541 | HPACK | HTTP/2用ヘッダー圧縮 |
| RFC 8446 | TLS 1.3 | TLS 1.3プロトコル |
| RFC 6455 | WebSocket Protocol | WebSocketプロトコル |
| RFC 6749 | OAuth 2.0 | OAuth 2.0認可フレームワーク |
| RFC 7519 | JWT | JSON Web Token |
| RFC 7807 | Problem Details | HTTPエラーレスポンスの標準形式 |
| RFC 8941 | Structured Field Values | 構造化ヘッダー値 |
| RFC 6797 | HSTS | HTTP Strict Transport Security |
| RFC 7540 | HTTP/2(初版) | HTTP/2の初期仕様 |
| RFC 2616 | HTTP/1.1(旧版) | HTTP/1.1の旧仕様(参考資料として) |
推奨書籍
- 「Real World HTTP ミニ版」 — 渋川よしき著(技術評論社)
- 「Webを支える技術 — HTTP、URI、HTML、そしてREST」 — 山本陽平著(技術評論社)
- 「HTTP: The Definitive Guide」 — David Gourley, Brian Totty(O'Reilly Media)
- 「High Performance Browser Networking」 — Ilya Grigorik(O'Reilly Media)
- 「Learning HTTP/2」 — Stephen Ludin, Javier Garza(O'Reilly Media)
オンラインリソース
- MDN Web Docs — HTTP: https://developer.mozilla.org/ja/docs/Web/HTTP
- HTTP/2 explained: https://http2-explained.haxx.se/
- HTTP/3 explained: https://http3-explained.haxx.se/
- IETF HTTP Working Group: https://httpwg.org/
- Can I Use(ブラウザ対応状況): https://caniuse.com/
- SSL Labs Server Test: https://www.ssllabs.com/ssltest/
- HTTP Archive: https://httparchive.org/
- web.dev by Google: https://web.dev/
本記事が、HTTPプロトコルの理解と実務での活用に役立てば幸いである。HTTPは30年以上の歴史を持つプロトコルであるが、HTTP/3やWebTransportなど、現在も活発に進化を続けている。最新のRFCやブラウザの実装状況を継続的にフォローし、適切な技術選択を行うことが重要である。