Workersリバースプロキシ:転送コードと信頼境界
文書番号:2601.00003
概要
Cloudflare Workers を使って リバースプロキシ(リバプロ) を実装する
全体像(通信フロー)
この実装では、 次の流れになるのだ。
クライアント
→ Cloudflare(Workers)
→ オリジン(area11.org)
←
←
Workers の中で fetch() を使い、
オリジンへリクエストを転送するのだ。
実装コード
以下が、今回のリバプロ実装なのだ。
// Cloudflare Workers: Reverse Proxy
// - ORIGIN_HOST を変数(環境変数)として使用
// - アクセス元IPを X-Forwarded-For / X-Real-IP 等に格納
// - Host ヘッダーもオリジン向けに調整
export default {
/**
* @param {Request} request
* @param {{ ORIGIN_HOST: string }} env
* @param {ExecutionContext} ctx
*/
async fetch(request, env, ctx) {
if (!env?.ORIGIN_HOST) {
return new Response("Missing env.ORIGIN_HOST", { status: 500 })
}
const incomingUrl = new URL(request.url)
// オリジン宛URLを組み立て
const originUrl = new URL(request.url)
originUrl.hostname = env.ORIGIN_HOST
// 必要なら originUrl.protocol = "https:" の固定も可能
// クライアントIP取得(Cloudflareが付与)
// 代表例: CF-Connecting-IP
const clientIp =
request.headers.get("CF-Connecting-IP") ||
request.headers.get("True-Client-IP") ||
""
// ヘッダを複製して加工
const headers = new Headers(request.headers)
// 1) Host をオリジン用に差し替え
headers.set("Host", env.ORIGIN_HOST)
// 2) X-Forwarded-For を追記(既存があれば末尾に付与)
// 例: "既存, clientIp"
if (clientIp) {
const xff = headers.get("X-Forwarded-For")
headers.set("X-Forwarded-For", xff ? `${xff}, ${clientIp}` : clientIp)
headers.set("X-Real-IP", clientIp)
}
// 3) Forwarded / X-Forwarded-* を整備
headers.set("X-Forwarded-Host", incomingUrl.host)
headers.set("X-Forwarded-Proto", incomingUrl.protocol.replace(":", ""))
headers.set(
"X-Forwarded-Port",
incomingUrl.port || (incomingUrl.protocol === "https:" ? "443" : "80"),
)
// 任意: 由来が分かるヘッダー(運用・デバッグ用)
headers.set("X-Proxy-By", "cloudflare-worker")
// ボディありメソッドは body を渡す。
// Request.body はストリームなので、
// new Request(originUrl, request) で引き継ぐ形が簡単。
const originRequest = new Request(originUrl.toString(), {
method: request.method,
headers,
body: shouldHaveBody(request.method) ? request.body : null,
redirect: "manual",
})
// オリジンへ転送
const originResponse = await fetch(originRequest)
// レスポンスをそのまま返す(必要ならヘッダー加工も可能)
// Set-Cookie 等を触る場合は attributes や domain に注意
return originResponse
},
}
function shouldHaveBody(method) {
return !["GET", "HEAD"].includes(method.toUpperCase())
}
コードのポイント解説
ORIGIN_HOSTを環境変数にしている
コード中の env.ORIGIN_HOST が、
転送先オリジンのホスト名なのだ。
環境ごとにオリジンを変えたい場合、 コードを書き換えずに切り替えられるのだ。
- クライアント IP は
CF-Connecting-IPを読む
Cloudflare はエッジで、
クライアント IP を CF-Connecting-IP として付与するのだ。
Workers 側では、
その値を X-Forwarded-For / X-Real-IP に入れてオリジンへ渡すのだ。
X-Forwarded-Forは、 既存値がある場合に既存, clientIpとして 追記 するのだ。- 追記にしておくと、 多段プロキシ構成でも経路を残せるのだ。
Hostをオリジン用に差し替える
オリジンがバーチャルホスト運用(Host でルーティング)している場合に効くのだ。
- 接続先(
originUrl.hostname) Hostヘッダー
を揃えると、 オリジン側は自然に処理できるのだ。
セキュリティ上の注意点(重要)
- WAF が無い場合、
Worker は入力検証を自動ではしてくれません
- 公開 API なら WAF / Rate Limit / 認証 を併用推奨
X-Forwarded-Forを信頼しすぎない- インターネットから来る
X-Forwarded-Forは偽装可能 - Workers では
CF-Connecting-IPを元に設定し、 オリジン側は「Cloudflare 経由のリクエストだけ」このヘッダーを信頼する設計にする
- インターネットから来る
- Cookie / 認証周り
Set-CookieのDomain/SameSiteは構成によって破綻しやすいので注意
結果
workerの設定

作成したリバプロ経由で無事アクセスできているのだ。

以上なのだ。
転送ヘッダーの信頼境界と確認する失敗経路
掲載コードは中継の学習例なのだ。受け取ったX-Forwarded-Forを追記するだけでは、その先頭値を認証済みの本人情報として扱えないのだ。信頼するプロキシ、オリジンへの直接到達、ヘッダーの上書き規則を定め、ログ用の値と認可に使う証拠を分けるのだ。
ORIGIN_HOSTは管理者が固定した許可先に限定し、自分自身へ戻る経路を作らないのだ。業務へ適用する前に、未知の転送先、ループ、タイムアウト、上流エラー、POST本文、CookieのDomain、Locationの戻り先を試験する必要があるのだ。本記事の画面確認はこれらの失敗経路を網羅した証拠ではないのだ。
Cloudflareのヘッダー仕様:https://developers.cloudflare.com/fundamentals/reference/http-headers/
二段構成で観測された値は多段プロキシの接続元観測に分けているのだ。