Chuyển đến nội dung chính

第4課:リバースプロキシ

Nginxにおけるリバースプロキシのコンセプトからproxy_pass設定、プロキシヘッダー、アップストリームサーバー、ヘルスチェックまでを学びます。 Node.js、Python、PHPなどのバックエンドアプリケーション向けにNginxをリバースプロキシとしてセットアップするガイドです。 ベストプラクティスとトラブルシューティングを含みます。

🔒 DevSecOps — 第4課 第4課:リバースプロキシ

Nginxの基礎から応用まで

第2部:リバースプロキシ & ロードバランシング

xdev.asia

1. リバースプロキシの概念

1.1. リバースプロキシとは?

リバースプロキシとは、クライアントとバックエンドサーバーの間に位置するサーバーです。クライアントからリクエストを受け取り、バックエンドサーバーに転送し、レスポンスをクライアントに返します。

違い:

フォワードプロキシ(クライアント側):
Client → Forward Proxy → Internet → Server
(クライアントを隠す)

リバースプロキシ(サーバー側): Client → Reverse Proxy → Backend Server (サーバーを隠す)

図解:

┌─────────┐         ┌──────────────┐         ┌──────────────┐
│         │         │              │         │              │
│ Client  │────────▶│    Nginx     │────────▶│   Backend    │
│         │         │ Reverse Proxy│         │   Server     │
│         │◀────────│              │◀────────│              │
└─────────┘         └──────────────┘         └──────────────┘

1.2. なぜリバースプロキシを使うのか?

1. ロードバランシング:

  • 複数のバックエンドサーバーへトラフィックを分散
  • 処理能力と信頼性の向上

2. SSL/TLSターミネーション:

  • NginxがSSL暗号化・復号を担う
  • バックエンドサーバーはHTTPSを気にしなくてよい

3. キャッシュ:

  • 静的コンテンツやAPIレスポンスをキャッシュ
  • バックエンドサーバーの負荷を削減

4. セキュリティ:

  • バックエンドサーバーのインフラを隠す
  • 保護レイヤー(レート制限、ファイアウォール)
  • 集中認証

5. 圧縮:

  • レスポンスのgzip圧縮
  • 帯域幅の削減

6. 静的ファイル配信:

  • Nginxが直接静的ファイルを配信
  • バックエンドは動的コンテンツのみ処理

7. 複数バックエンド:

  • 異なるアプリケーションへリクエストをルーティング
  • マイクロサービスアーキテクチャ

1.3. よくあるユースケース

1. シングルページアプリケーション(SPA):
/          → React/Vue/Angular アプリ
/api/*     → バックエンドAPIサーバー

  1. マイクロサービス: /users/* → ユーザーサービス /orders/* → 注文サービス /payments/* → 決済サービス

  2. 複数アプリケーション: site.com → メインサイト blog.site.com → WordPress ブログ api.site.com → API サーバー

  3. レガシー + 新規: /old/* → レガシーPHPアプリケーション /new/* → 新しいNode.jsアプリケーション


2. proxy_passの基本設定

2.1. proxy_passの書き方

location /path/ {
proxy_pass http://backend_server;
}

シンプルな例:

server {
listen 80;
server_name example.com;

location / { # すべてのリクエストをバックエンドに転送 proxy_pass http://localhost:3000; } }

2.2. URIを使ったproxy_pass

方法1:末尾スラッシュなし

location /api {
proxy_pass http://localhost:3000;
}

リクエスト: /api/users

転送先: http://localhost:3000/api/users

(/apiプレフィックスを保持)

方法2:末尾スラッシュあり

location /api/ {
proxy_pass http://localhost:3000/;
}

リクエスト: /api/users

転送先: http://localhost:3000/users

(/apiプレフィックスを削除)

方法3:特定パスを指定

location /api/ {
proxy_pass http://localhost:3000/v1/;
}

リクエスト: /api/users

転送先: http://localhost:3000/v1/users

(/apiを/v1に置き換え)

詳細な例:

server {
listen 80;
server_name example.com;

# ルートのプロキシ
location / {
    proxy_pass http://localhost:3000;
}

# APIのプロキシ(/apiプレフィックスを削除)
location /api/ {
    proxy_pass http://localhost:4000/;
}

# adminのプロキシ(/adminプレフィックスを保持)
location /admin {
    proxy_pass http://localhost:5000;
}

# 完全一致でプロキシ
location = /health {
    proxy_pass http://localhost:3000/healthcheck;
}

}

2.3. 複数バックエンドへのプロキシ

server {
listen 80;
server_name example.com;

# フロントエンドSPA
location / {
    root /var/www/html;
    try_files $uri $uri/ /index.html;
}

# APIバックエンド
location /api/ {
    proxy_pass http://localhost:3000/;
}

# 認証サービス
location /auth/ {
    proxy_pass http://localhost:4000/;
}

# WebSocketサーバー
location /ws/ {
    proxy_pass http://localhost:5000/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

# 静的アセット(CDN)
location /static/ {
    proxy_pass http://cdn.example.com/;
}

}

2.4. 変数を使ったプロキシ

server {
listen 80;
server_name example.com;

# サブドメインに基づくプロキシ
location / {
    proxy_pass http://$http_host$request_uri;
}

# カスタム変数でプロキシ
set $backend "localhost:3000";
location /api/ {
    proxy_pass http://$backend/;
}

# 条件付きプロキシ
location /dynamic/ {
    if ($arg_version = "v2") {
        proxy_pass http://localhost:4000/;
    }
    proxy_pass http://localhost:3000/;
}

}

2.5. プロキシタイムアウト

location /api/ {
proxy_pass http://localhost:3000/;

# タイムアウト設定
proxy_connect_timeout 60s;      # アップストリームへの接続タイムアウト
proxy_send_timeout 60s;         # リクエスト送信タイムアウト
proxy_read_timeout 60s;         # レスポンス読み取りタイムアウト

# バッファ設定
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;

}


3. プロキシヘッダー

ヘッダーはバックエンドサーバーが元のリクエストの情報を知るために重要です。

3.1. 必須プロキシヘッダー

location / {
proxy_pass http://localhost:3000;

# Hostヘッダー
proxy_set_header Host $host;

# クライアントの実IPアドレス
proxy_set_header X-Real-IP $remote_addr;

# プロキシのチェーン
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# プロトコル(http/https)
proxy_set_header X-Forwarded-Proto $scheme;

# 元のホスト
proxy_set_header X-Forwarded-Host $host;

# ポート
proxy_set_header X-Forwarded-Port $server_port;

}

3.2. ヘッダーの説明

Host:

proxy_set_header Host $host;

$host = リクエストのドメイン名

例: example.com

バックエンドが受け取る: Host: example.com

X-Real-IP:

proxy_set_header X-Real-IP $remote_addr;

$remote_addr = Nginxに直接接続しているクライアントのIP

例: 192.168.1.100

バックエンドが受け取る: X-Real-IP: 192.168.1.100

X-Forwarded-For:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

既存のX-Forwarded-ForヘッダーにクライアントIPを追記

例:

リクエスト1: Client → Nginx → Backend

X-Forwarded-For: 192.168.1.100

リクエスト2: Client → CDN → Nginx → Backend

X-Forwarded-For: 192.168.1.100, 10.0.0.50

$proxy_add_x_forwarded_forはプロキシのチェーンを保持

X-Forwarded-Proto:

proxy_set_header X-Forwarded-Proto $scheme;

$scheme = http または https

バックエンドが元のリクエストがHTTPかHTTPSかを判断できる

リダイレクトロジックに重要

3.3. 完全なヘッダー設定

http {
# ヘッダーテンプレートを定義
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-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;

server {
    listen 80;
    server_name example.com;
    
    location / {
        proxy_pass http://localhost:3000;
        # httpコンテキストからヘッダーを継承
    }
}

}

3.4. カスタムヘッダー

location /api/ {
proxy_pass http://localhost:3000/;

# 標準ヘッダー
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_set_header X-Server-Name $hostname;
proxy_set_header X-Forwarded-User $remote_user;

# ヘッダーを削除
proxy_set_header Authorization "";  # 認証ヘッダーを削除

# カスタム値を追加
proxy_set_header X-API-Version "v1";
proxy_set_header X-Environment "production";

}

3.5. WebSocket用ヘッダー

location /ws/ {
proxy_pass http://localhost:3000/;

# WebSocket専用ヘッダー
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;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# WebSocketタイムアウト
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;

}

3.6. 元のヘッダーを保持する

location / {
proxy_pass http://localhost:3000;

# すべての元のヘッダーを転送
proxy_pass_request_headers on;

# 特定ヘッダー
proxy_set_header Accept-Encoding $http_accept_encoding;
proxy_set_header Accept-Language $http_accept_language;
proxy_set_header Cookie $http_cookie;
proxy_set_header Referer $http_referer;
proxy_set_header User-Agent $http_user_agent;

}

3.7. セキュリティヘッダー

location / {
proxy_pass http://localhost:3000;

# 標準プロキシヘッダー
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# Nginxバージョンを隠す
proxy_hide_header X-Powered-By;
proxy_hide_header Server;

# セキュリティヘッダーを追加
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 Referrer-Policy "no-referrer-when-downgrade" always;

}


4. アップストリームサーバーとロードバランシング

4.1. 基本のupstreamブロック

# upstreamを定義
upstream backend {
server localhost:3000;
server localhost:3001;
server localhost:3002;
}

server { listen 80; server_name example.com;

location / {
    proxy_pass http://backend;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

}

4.2. ロードバランシングの方式

1. ラウンドロビン(デフォルト):

upstream backend {
# ラウンドロビン:各サーバーに順番に
server localhost:3000;
server localhost:3001;
server localhost:3002;
}

リクエスト1 → 3000

リクエスト2 → 3001

リクエスト3 → 3002

リクエスト4 → 3000(繰り返し)

2. 最少コネクション:

upstream backend {
least_conn;  # 接続数が最も少ないサーバー

server localhost:3000;
server localhost:3001;
server localhost:3002;

}

長時間接続に適している

負荷を均等に分散

3. IPハッシュ(スティッキーセッション):

upstream backend {
ip_hash;  # 同じクライアント → 同じサーバー

server localhost:3000;
server localhost:3001;
server localhost:3002;

}

クライアント192.168.1.100 → 常にサーバー3000へ

クライアント192.168.1.101 → 常にサーバー3001へ

セッションベースのアプリケーションに適している

4. ハッシュ(汎用):

upstream backend {
hash $request_uri consistent;  # URIでハッシュ

server localhost:3000;
server localhost:3001;
server localhost:3002;

}

同じURI → 同じサーバー

キャッシュに適している

5. ランダム:

upstream backend {
random;  # ランダムにサーバーを選択

server localhost:3000;
server localhost:3001;
server localhost:3002;

}

4.3. サーバーウェイト

upstream backend {
# ウェイトが高いサーバーほど多くのリクエストを受け取る
server localhost:3000 weight=3;  # 60%のトラフィック
server localhost:3001 weight=1;  # 20%のトラフィック
server localhost:3002 weight=1;  # 20%のトラフィック
}

合計ウェイト = 5

サーバー3000: 3/5 = 60%

サーバー3001: 1/5 = 20%

サーバー3002: 1/5 = 20%

ウェイトのユースケース:

upstream backend {
# 本番サーバー
server prod1.example.com weight=5;
server prod2.example.com weight=5;

# カナリアデプロイメント - 10%のトラフィック
server canary.example.com weight=1;

}

4.4. バックアップサーバー

upstream backend {
server localhost:3000;
server localhost:3001;
server localhost:3002 backup;  # プライマリサーバーがダウンしたときのみ使用
}

3002は3000と3001が両方使用不可になったときのみトラフィックを受け取る

4.5. サーバーパラメーター

upstream backend {
server localhost:3000 weight=5 max_fails=3 fail_timeout=30s;
server localhost:3001 weight=5 max_fails=3 fail_timeout=30s;
server localhost:3002 backup;
server localhost:3003 down;  # 一時的に無効
}

パラメーター:

weight=N - ウェイト(デフォルト1)

max_fails=N - ダウンとしてマークするまでの失敗回数(デフォルト1)

fail_timeout=T - タイムアウト時間(デフォルト10s)

backup - バックアップサーバー

down - 一時的に無効

4.6. 高度なupstream設定

upstream backend {
least_conn;  # ロードバランシング方式

# サーバー設定
server srv1.example.com:8080 weight=3 max_fails=2 fail_timeout=30s;
server srv2.example.com:8080 weight=3 max_fails=2 fail_timeout=30s;
server srv3.example.com:8080 weight=2 max_fails=2 fail_timeout=30s;
server srv4.example.com:8080 backup;

# キープアライブ接続
keepalive 32;  # アップストリームへの32個のアイドル接続を保持
keepalive_timeout 60s;
keepalive_requests 100;

}

server { listen 80;

location / {
    proxy_pass http://backend;
    
    # キープアライブ用HTTPバージョン
    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;
}

}

4.7. 複数のupstream

# APIバックエンド
upstream api_backend {
least_conn;
server api1.example.com:3000;
server api2.example.com:3000;
server api3.example.com:3000;
}

認証サービス

upstream auth_backend { server auth1.example.com:4000; server auth2.example.com:4000; }

WebSocketサービス

upstream websocket_backend { ip_hash; # WebSocket用スティッキーセッション server ws1.example.com:5000; server ws2.example.com:5000; }

server { listen 80; server_name example.com;

location /api/ {
    proxy_pass http://api_backend/;
}

location /auth/ {
    proxy_pass http://auth_backend/;
}

location /ws/ {
    proxy_pass http://websocket_backend/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

}


5. 基本的なヘルスチェック

5.1. パッシブヘルスチェック

Nginxはレスポンスに基づいて自動的に障害サーバーを検出します。

upstream backend {
server localhost:3000 max_fails=3 fail_timeout=30s;
server localhost:3001 max_fails=3 fail_timeout=30s;
server localhost:3002 max_fails=3 fail_timeout=30s;
}

max_fails=3: 3回連続失敗した後

fail_timeout=30s: サーバーは30秒間ダウンとしてマーク

30秒後、Nginxはサーバーを再試行

動作の詳細:

1. localhost:3000にリクエストを送信
2. サーバーが502、503、504を返すかタイムアウト → 失敗カウント = 1
3. 次のリクエストを3000に送信
4. サーバーが再び失敗 → 失敗カウント = 2
5. 次のリクエストを3000に送信
6. サーバーが3回目の失敗 → 失敗カウント = 3 → サーバーがDOWNとしてマーク
7. トラフィックは3001と3002にルーティング
8. 30秒後、Nginxは3000を再試行
9. 3000がOKで応答 → 失敗カウントリセット、サーバーUP

5.2. アクティブヘルスチェック(Nginx Plus)

Nginx Plusはアクティブヘルスチェックをサポートします(オープンソース版には含まれません)。

# Nginx Plusのみ
upstream backend {
zone backend 64k;
server localhost:3000;
server localhost:3001;
server localhost:3002;
}

server { listen 80;

location / {
    proxy_pass http://backend;
    health_check interval=5s fails=3 passes=2 uri=/health;
}

}

interval=5s: 5秒ごとにチェック

fails=3: 3回失敗でダウンとしてマーク

passes=2: 2回成功でアップとしてマーク

uri=/health: チェック用エンドポイント

5.3. カスタムヘルスチェックエンドポイント

バックエンドの実装(Node.jsの例):

// health.js
const express = require('express');
const app = express();

app.get('/health', (req, res) => { // データベース接続を確認 // 依存関係を確認 // メモリ使用量を確認など

const health = {
    status: 'ok',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    memory: process.memoryUsage()
};

res.status(200).json(health);

});

app.listen(3000);

Nginx設定:

upstream backend {
server localhost:3000 max_fails=3 fail_timeout=30s;
server localhost:3001 max_fails=3 fail_timeout=30s;
}

server { listen 80;

location / {
    proxy_pass http://backend;
}

# ヘルスチェックエンドポイント(公開しない)
location /health {
    access_log off;
    proxy_pass http://backend;
    
    # localhostからのみ許可
    allow 127.0.0.1;
    deny all;
}

}

5.4. 外部ヘルスチェック

外部スクリプトでアップストリームを監視・更新します。

監視スクリプト:

#!/bin/bash

health_check.sh

UPSTREAM_SERVERS=( "localhost:3000" "localhost:3001" "localhost:3002" )

HEALTH_ENDPOINT="/health"

for server in "${UPSTREAM_SERVERS[@]}"; do response=$(curl -s -o /dev/null -w "%{http_code}" "http://$server$HEALTH_ENDPOINT")

if [ "$response" = "200" ]; then
    echo "$(date) - $server is healthy"
else
    echo "$(date) - $server is down (HTTP $response)"
    # アラートを送信
    # upstream設定を更新
    # Nginxをリロード
fi

done

Crontab:

# 毎分ヘルスチェックを実行

          • /usr/local/bin/health_check.sh >> /var/log/health_check.log 2>&1

5.5. stub_statusによる監視

server {
listen 8080;
server_name localhost;





location /nginx_status { stub_status; access_log off; allow 127.0.0.1; deny all; } }

ステータスを確認:

curl http://localhost:8080/nginx_status

出力:

Active connections: 291

server accepts handled requests

16630948 16630948 31070465

Reading: 6 Writing: 179 Waiting: 106

5.6. スクリプトによるヘルスチェック

Pythonヘルスチェック:

#!/usr/bin/env python3

health_monitor.py

import requests import time import smtplib from email.message import EmailMessage

BACKENDS = [ 'http://localhost:3000/health', 'http://localhost:3001/health', 'http://localhost:3002/health', ]

def check_health(url): try: response = requests.get(url, timeout=5) return response.status_code == 200 except: return False

def send_alert(backend, status): msg = EmailMessage() msg['Subject'] = f'Backend Alert: {backend}' msg['From'] = '[email protected]' msg['To'] = '[email protected]' msg.set_content(f'Backend {backend} is {status}')

with smtplib.SMTP('localhost') as s:
    s.send_message(msg)

def main(): while True: for backend in BACKENDS: if not check_health(backend): print(f'{backend} is DOWN') send_alert(backend, 'DOWN') else: print(f'{backend} is UP')

    time.sleep(60)  # 毎分チェック

if name == 'main': main()


6. 実際の例

6.1. Node.jsアプリケーション

バックエンド(app.js):

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => { res.json({ message: 'Hello from Node.js', server: localhost:${PORT}, headers: req.headers }); });

app.get('/api/users', (req, res) => { res.json([ { id: 1, name: 'User 1' }, { id: 2, name: 'User 2' } ]); });

app.get('/health', (req, res) => { res.status(200).json({ status: 'ok' }); });

app.listen(PORT, () => { console.log(Server running on port ${PORT}); });

Nginx設定:

upstream nodejs_backend {
least_conn;
server localhost:3000 max_fails=3 fail_timeout=30s;
server localhost:3001 max_fails=3 fail_timeout=30s;
server localhost:3002 max_fails=3 fail_timeout=30s;
keepalive 32;
}

server { listen 80; server_name api.example.com;

access_log /var/log/nginx/nodejs.access.log;
error_log /var/log/nginx/nodejs.error.log;

location / {
    proxy_pass http://nodejs_backend;
    
    # HTTPバージョン
    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_set_header Connection "";
    
    # タイムアウト
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;
    
    # バッファリング
    proxy_buffering on;
    proxy_buffer_size 4k;
    proxy_buffers 8 4k;
}

location /health {
    access_log off;
    proxy_pass http://nodejs_backend/health;
}

}

6.2. Python Flask/Djangoアプリケーション

バックエンド(app.py):

from flask import Flask, jsonify, request
import os

app = Flask(name) PORT = int(os.environ.get('PORT', 5000))

@app.route('/') def home(): return jsonify({ 'message': 'Hello from Python', 'server': f'localhost:{PORT}', 'headers': dict(request.headers) })

@app.route('/api/data') def get_data(): return jsonify([ {'id': 1, 'value': 'Data 1'}, {'id': 2, 'value': 'Data 2'} ])

@app.route('/health') def health(): return jsonify({'status': 'ok'}), 200

if name == 'main': app.run(host='0.0.0.0', port=PORT)

Nginx設定:

upstream python_backend {
server localhost:5000;
server localhost:5001;
server localhost:5002;
}

server { listen 80; server_name python.example.com;

client_max_body_size 10M;

location / {
    proxy_pass http://python_backend;
    
    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;
    
    # Pythonアプリは遅い場合がある
    proxy_read_timeout 300s;
    proxy_connect_timeout 300s;
    proxy_send_timeout 300s;
}

}

6.3. PHP-FPMを使ったPHPアプリケーション

Nginx設定:

upstream php_backend {
server unix:/var/run/php/php8.1-fpm.sock;
# または
# server localhost:9000;
}

server { listen 80; server_name php.example.com; root /var/www/php; index index.php index.html;

location / {
    try_files $uri $uri/ /index.php?$args;
}

location ~ \.php$ {
    include fastcgi_params;
    fastcgi_pass php_backend;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    
    # ヘッダー
    fastcgi_param HTTP_X_REAL_IP $remote_addr;
    fastcgi_param HTTP_X_FORWARDED_FOR $proxy_add_x_forwarded_for;
    fastcgi_param HTTP_X_FORWARDED_PROTO $scheme;
}

location ~ /\.ht {
    deny all;
}

}

6.4. マイクロサービスアーキテクチャ

# ユーザーサービス
upstream user_service {
server user1.internal:8001;
server user2.internal:8001;
}

注文サービス

upstream order_service { server order1.internal:8002; server order2.internal:8002; }

決済サービス

upstream payment_service { server payment1.internal:8003; server payment2.internal:8003; }

商品サービス

upstream product_service { server product1.internal:8004; server product2.internal:8004; }

server { listen 80; server_name api.example.com;

# 共通ヘッダー
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;

location /api/users/ {
    proxy_pass http://user_service/;
}

location /api/orders/ {
    proxy_pass http://order_service/;
}

location /api/payments/ {
    proxy_pass http://payment_service/;
}

location /api/products/ {
    proxy_pass http://product_service/;
}

}


7. 実践演習

演習1:基本的なリバースプロキシ

  1. ポート3000でシンプルなNode.js/Pythonサーバーを作成する
  2. NginxをリバースプロキシとしてConfigure する
  3. ヘッダーが正しく転送されているかテスト・確認する

演習2:複数バックエンド

  1. ポート3000、3001、3002でアプリケーションの3インスタンスを起動する
  2. ラウンドロビンでupstreamを設定する
  3. ロードバランシングをテストする(ログを確認してリクエストの分散を確認)

演習3:スティッキーセッション

  1. ip_hashでupstreamを設定する
  2. 同じクライアントが常に同じバックエンドに接続されることをテストする
  3. ラウンドロビンと比較する

演習4:ヘルスチェック

  1. max_failsとfail_timeoutでパッシブヘルスチェックを設定する
  2. バックエンドサーバーを1台停止する
  3. Nginxが自動的にヘルシーなサーバーにトラフィックをルーティングすることを確認する
  4. サーバーを再起動してトラフィックが戻ることを確認する

演習5:マイクロサービス

  1. 2〜3つのシンプルなAPI(モックでもよい)を作成する
  2. URLパスに基づいてリクエストをルーティングするようNginxを設定する:
    • /api/users → ユーザーサービス
    • /api/products → 商品サービス
  3. ルーティングをテストする

演習6:WebSocketプロキシ

  1. シンプルなWebSocketサーバーを作成する
  2. NginxをWebSocketプロキシとして設定する
  3. 接続とメッセージ送受信をテストする

8. トラブルシューティング

8.1. よくある問題

1. 502 Bad Gateway:

# 原因:バックエンドが起動していないまたは到達できない

バックエンドを確認

curl http://localhost:3000

Nginxエラーログを確認

sudo tail -f /var/log/nginx/error.log

ファイアウォールを確認

sudo ufw status

2. 504 Gateway Timeout:

# 原因:バックエンドの処理が遅すぎる

修正:タイムアウトを延長

location / { proxy_pass http://backend; proxy_read_timeout 300s; proxy_connect_timeout 300s; }

3. ヘッダーが転送されない:

# バックエンドでヘッダーを確認

リクエストヘッダーをログに記録する

Nginx設定

proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

4. 大きなファイルのアップロードが失敗する:

# client_max_body_sizeを増やす
http {
client_max_body_size 100M;
}

5. WebSocket接続が失敗する:

# upgradeヘッダーが必要
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

8.2. デバッグコマンド

# アップストリームの接続テスト
curl -v http://localhost:3000

Nginx設定を確認

sudo nginx -t

Nginxをリロード

sudo systemctl reload nginx

エラーログをリアルタイムで確認

sudo tail -f /var/log/nginx/error.log

アップストリームのステータスを確認(stub_statusが有効な場合)

curl http://localhost/nginx_status

特定ヘッダーでテスト

curl -H "Host: example.com" http://localhost

プロキシヘッダーでテスト

curl -H "X-Forwarded-For: 1.2.3.4" http://localhost


9. ベストプラクティス

9.1. 設定

  1. upstreamブロックを使用する:
# 推奨
upstream backend {
server localhost:3000;
}

非推奨(フェイルオーバーやロードバランシングなし)

location / { proxy_pass http://localhost:3000; }

  1. 適切なタイムアウトを設定する:
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
  1. キープアライブを有効にする:
upstream backend {
server localhost:3000;
keepalive 32;
}

location / { proxy_http_version 1.1; proxy_set_header Connection ""; }

  1. ヘルスチェックを使用する:
server localhost:3000 max_fails=3 fail_timeout=30s;

9.2. セキュリティ

  1. 内部構造を公開しない:
proxy_hide_header X-Powered-By;
  1. リクエストサイズを制限する:
client_max_body_size 10M;
  1. レート制限:
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

location /api/ { limit_req zone=api burst=20; }

9.3. パフォーマンス

  1. バッファ設定:
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
  1. キャッシュ:
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m;

location / { proxy_cache my_cache; proxy_cache_valid 200 10m; }


まとめ

この課では以下を学びました:

  • ✅ リバースプロキシの概念とユースケース
  • ✅ proxy_passの設定とルーティング
  • ✅ プロキシヘッダーとX-Forwardedヘッダー
  • ✅ アップストリームサーバーとロードバランシング
  • ✅ ヘルスチェックとモニタリング
  • ✅ Node.js、Python、PHPを使った実例

次の課: ロードバランシングをさらに深掘りします——アルゴリズム、ストラテジー、高度な設定について学びます。