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 1945HTTP/1.0HTTP/1.0の仕様
RFC 2616HTTP/1.1HTTP/1.1の初期仕様(現在は廃止)
RFC 7230-7235HTTP/1.1(改訂版)HTTP/1.1の現行仕様(6分冊)
RFC 7540HTTP/2HTTP/2の初期仕様
RFC 9110HTTP SemanticsHTTPの意味論(メソッド、ステータスコード等)
RFC 9111HTTP CachingHTTPキャッシュの仕様
RFC 9112HTTP/1.1HTTP/1.1メッセージ構文の最新仕様
RFC 9113HTTP/2HTTP/2の最新仕様
RFC 9114HTTP/3HTTP/3の仕様
RFC 9204QPACKHTTP/3用ヘッダー圧縮

2022年に公開されたRFC 9110〜9114は、HTTPの仕様体系を整理し直したものであり、プロトコルのバージョンに依存しない「HTTP Semantics」(RFC 9110)と、各バージョン固有の仕様(RFC 9112〜9114)に分離された。これにより、HTTPの本質的な意味論と、その転送方法を明確に区別できるようになった。

1.5 本記事の構成

本記事では、HTTPプロトコルを以下の観点から包括的に解説する。

  1. HTTPの基本アーキテクチャとメッセージ構造
  2. リクエストとレスポンスの詳細
  3. HTTP/1.1、HTTP/2、HTTP/3の各バージョンの特徴と設定
  4. セキュリティ(HTTPS/TLS)
  5. キャッシュ制御
  6. 認証と認可
  7. REST APIの設計
  8. WebSocket、SSE、gRPCとの関係
  9. 監視、デバッグ、トラブルシューティング
  10. ロードバランサーとリバースプロキシ

各章では概念の解説に加え、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のリクエスト/レスポンスサイクルは以下の手順で行われる。

  1. DNS解決: クライアントがドメイン名をIPアドレスに解決する
  2. TCP接続確立: 3ウェイハンドシェイクによりTCP接続を確立する(HTTP/3ではQUIC接続)
  3. TLSハンドシェイク: HTTPS通信の場合、TLSハンドシェイクを実行する
  4. HTTPリクエスト送信: クライアントがHTTPリクエストメッセージを送信する
  5. サーバー処理: サーバーがリクエストを処理する
  6. HTTPレスポンス送信: サーバーがHTTPレスポンスメッセージを返送する
  7. 接続管理: 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)

サーバーがリクエストを受信し、処理を継続中であることを示す。

コード名前説明
100Continueリクエストの続行を許可。Expect: 100-continueヘッダーへの応答
101Switching Protocolsプロトコルの切り替え(WebSocketアップグレード等)
102Processingリクエスト処理中(WebDAV)
103Early 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)

リクエストが正常に処理されたことを示す。

コード名前説明
200OKリクエスト成功(最も一般的)
201Createdリソースが正常に作成された
202Acceptedリクエストを受け付けたが、処理は未完了
204No Content成功したが、レスポンスボディなし
206Partial 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)

リクエストを完了するために追加のアクションが必要であることを示す。

コード名前説明
301Moved Permanentlyリソースが恒久的に移動(SEOに影響)
302Foundリソースが一時的に移動
303See Other別のURIをGETで参照すべき
304Not Modifiedリソースが変更されていない(キャッシュ利用可)
307Temporary Redirect一時的リダイレクト(メソッドを維持)
308Permanent 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)

クライアント側のエラーを示す。

コード名前説明
400Bad Requestリクエストの構文が不正
401Unauthorized認証が必要(未認証)
403Forbiddenアクセス権限がない(認証済みだが認可されていない)
404Not Foundリソースが存在しない
405Method Not Allowed許可されていないHTTPメソッド
408Request Timeoutリクエストのタイムアウト
409Conflictリソースの競合(楽観的ロック失敗等)
413Content Too Largeリクエストボディが大きすぎる
415Unsupported Media TypeサポートされていないContent-Type
422Unprocessable Contentバリデーションエラー
429Too 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)

サーバー側のエラーを示す。

コード名前説明
500Internal Server Errorサーバー内部エラー
501Not Implemented機能が未実装
502Bad Gatewayゲートウェイ/プロキシが不正なレスポンスを受信
503Service Unavailableサービスが一時的に利用不可(メンテナンス等)
504Gateway 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 (可変長)               │
└──────────────────────────────────────┘

主なフレームタイプ:

タイプ説明
DATA0x0リクエスト/レスポンスのボディデータ
HEADERS0x1ヘッダーフィールドのブロック
PRIORITY0x2ストリームの優先度(HTTP/2のみ。RFC 9113では非推奨)
RST_STREAM0x3ストリームの即時終了
SETTINGS0x4接続設定パラメータ
PUSH_PROMISE0x5サーバープッシュの予告
PING0x6接続の生存確認とRTT計測
GOAWAY0x7接続の終了通知
WINDOW_UPDATE0x8フロー制御ウィンドウの更新
CONTINUATION0x9HEADERSフレームの継続

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の仕組み:

  1. 静的テーブル: 61個の事前定義されたヘッダーフィールド(:method: GET, :status: 200等)
  2. 動的テーブル: 接続中に学習したヘッダーフィールドのキャッシュ
  3. ハフマン符号化: ヘッダー値のバイナリ圧縮
# 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 バイト          │
└────────────────────────────────────────────────┘

フロー制御の仕組み:

  1. 各ストリームと接続にはウィンドウサイズがある(デフォルト65,535バイト)
  2. DATAフレーム送信ごとにウィンドウサイズが減少する
  3. 受信側がWINDOW_UPDATEフレームを送信してウィンドウを拡大する
  4. ウィンドウサイズが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/2HTTP/3
トランスポートTCPQUIC (UDP)
暗号化オプション(実質TLS必須)必須(TLS 1.3内蔵)
多重化ストリーム多重化ストリーム多重化
HoL BlockingTCP層で発生する完全に解消
ヘッダー圧縮HPACKQPACK
接続確立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導入時の考慮事項

  1. UDPのブロック: 企業のファイアウォールでUDPポート443がブロックされている場合がある。HTTP/2へのフォールバック機構が必要
  2. UDPの処理能力: サーバーやネットワーク機器のUDP処理能力がTCPより低い場合がある
  3. CPUオーバーヘッド: QUICの暗号化処理はカーネルではなくユーザースペースで実行されるため、CPU使用率が高くなる可能性がある
  4. モニタリング: 既存のTCP監視ツールがQUIC/UDPに対応していない場合がある
  5. 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 10HTTPレベルの対策
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設定

属性説明推奨値
HttpOnlyJavaScriptからのアクセスを禁止(XSS対策)必須
SecureHTTPS接続でのみ送信必須
SameSiteクロスサイトリクエストでの送信制御(CSRF対策)Strict または Lax
PathCookieが送信されるパス/
Max-Age / Expires有効期限用途に応じて設定
DomainCookieが送信されるドメイン明示的に設定

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/SessionWebアプリケーション中〜高
OAuth 2.0サードパーティ連携
JWTSPA、マイクロサービス中〜高
mTLSマイクロサービス間通信非常に高

第11章: REST APIの設計とHTTP

11.1 RESTアーキテクチャスタイルの原則

REST(Representational State Transfer)は、2000年にRoy Fieldingが博士論文で提唱したアーキテクチャスタイルであり、以下の6つの制約を定義している。

  1. クライアント・サーバー分離: UIとデータストレージの関心事を分離
  2. ステートレス: 各リクエストはそれ自体で完結し、サーバーにセッション状態を保持しない
  3. キャッシュ可能: レスポンスをキャッシュ可能として明示的にマークできる
  4. 統一インターフェース: リソースの識別、表現によるリソース操作、自己記述的メッセージ、HATEOAS
  5. 階層化システム: クライアントはエンドサーバーに直接接続しているか中間者を経由しているか認識しない
  6. コードオンデマンド(オプション): サーバーがクライアントに実行可能コードを送信できる

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例ステータスコード
CreatePOSTPOST /users201 Created
Read (一覧)GETGET /users200 OK
Read (詳細)GETGET /users/123200 OK
Update (全体)PUTPUT /users/123200 OK
Update (部分)PATCHPATCH /users/123200 OK
DeleteDELETEDELETE /users/123204 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)WebSocketSSEgRPC
通信方向リクエスト・レスポンス双方向サーバー→クライアント双方向
プロトコルHTTP/1.1, HTTP/2WS/WSSHTTP/1.1, HTTP/2HTTP/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, NLBNginx, 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 9110HTTP SemanticsHTTPの意味論(メソッド、ステータスコード、ヘッダー)
RFC 9111HTTP CachingHTTPキャッシュ
RFC 9112HTTP/1.1HTTP/1.1メッセージ構文
RFC 9113HTTP/2HTTP/2プロトコル
RFC 9114HTTP/3HTTP/3プロトコル
RFC 9000QUICQUICトランスポートプロトコル
RFC 9001Using TLS to Secure QUICQUICのTLS統合
RFC 9204QPACKHTTP/3用ヘッダー圧縮
RFC 7541HPACKHTTP/2用ヘッダー圧縮
RFC 8446TLS 1.3TLS 1.3プロトコル
RFC 6455WebSocket ProtocolWebSocketプロトコル
RFC 6749OAuth 2.0OAuth 2.0認可フレームワーク
RFC 7519JWTJSON Web Token
RFC 7807Problem DetailsHTTPエラーレスポンスの標準形式
RFC 8941Structured Field Values構造化ヘッダー値
RFC 6797HSTSHTTP Strict Transport Security
RFC 7540HTTP/2(初版)HTTP/2の初期仕様
RFC 2616HTTP/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)

オンラインリソース


本記事が、HTTPプロトコルの理解と実務での活用に役立てば幸いである。HTTPは30年以上の歴史を持つプロトコルであるが、HTTP/3やWebTransportなど、現在も活発に進化を続けている。最新のRFCやブラウザの実装状況を継続的にフォローし、適切な技術選択を行うことが重要である。