Claude Media
forward_user_identityで自前プロキシへユーザーIDを渡す

forward_user_identityで自前プロキシへユーザーIDを渡す

Claude apps gatewayの背後にLiteLLMなど自前プロキシを置くとき、upstreamにforward_user_identityを付けて開発者のIDとメールをヘッダーで渡す設定と、429の扱い、起動拒否の条件です。

Claude apps gatewayは、既定ではどの開発者のリクエストかをupstreamに伝えません。自前のプロキシ(LiteLLMなど)を挟んで開発者ごとに利用量を数えたいなら、そのupstreamに forward_user_identity: true を付けます。ゲートウェイが開発者のIDとメールをヘッダーとして付けて転送するようになります。

設定は1行です。ただし、ヘッダーの中身、メールが無いIdPの扱い、プロキシが返す 429 の扱い、起動を拒否される条件は、あらかじめ知っておかないと運用で迷います。

forward_user_identityを有効にする

対象は provider: anthropic のupstreamで、base_url を自分が運用するプロキシに向けたものです。そこに forward_user_identity: true を足します。既定値は false です。

upstreams:
  - provider: anthropic
    base_url: https://upstream-gateway.internal.example.com
    auth:
      api_key: ${PROXY_KEY}
    forward_user_identity: true        # 既定は false

auth.api_key には、プロキシがゲートウェイに要求する鍵を入れます。プロキシ側の認証方式によって中身は変わります。

この設定を使えるのは、Claude Code v2.1.233以降のゲートウェイです。変更履歴では、2026年8月14日のv2.1.233で追加されたオプトイン設定として載っています。

有効にする前の確認点

  • 対象は provider: anthropic のupstreamだけ
  • base_url は自分で運用するプロキシにする
  • ゲートウェイはv2.1.233以降
  • 既定は false なので、付けなければ何も送られない

付与される3つのヘッダー

有効にすると、ゲートウェイはそのupstreamへ転送する全リクエストに次のヘッダーを付けます。

ヘッダー値
x-litellm-end-user-id値開発者のメール(IdPがメールを渡したとき)
x-claude-gateway-user-id値IdPのsubject(トークンの sub クレーム)
x-claude-gateway-user-email値開発者のメール(IdPがメールを渡したとき)

x-litellm-end-user-id にはメールが入ります。名前のとおりLiteLLMが読むヘッダー名ですが、LiteLLM側でこのヘッダーを支出集計にどう使うかは、LiteLLMの設定次第です。設定方法はLiteLLMのドキュメントにあります。

LiteLLM以外のプロキシは、x-claude-gateway-user-id と x-claude-gateway-user-email を読んで開発者を判別します。

届いたヘッダーを確かめる

本番のプロキシに向ける前に、受け取ったヘッダーを表示するだけの簡単なサーバーを立てておくと、IdPのトークンからどの値が出ているかを確かめられます。次はそのための最小の例です。ゲートウェイからの転送を受けるには base_url をこのサーバーに向けます。

# 受け取った3つのヘッダーを標準出力に出すだけの確認用サーバー
from http.server import BaseHTTPRequestHandler, HTTPServer
 
NAMES = ["x-litellm-end-user-id",
         "x-claude-gateway-user-id",
         "x-claude-gateway-user-email"]
 
class H(BaseHTTPRequestHandler):
    def do_POST(self):
        for n in NAMES:
            print(n, "=", self.headers.get(n))
        self.send_response(200)
        self.end_headers()
 
HTTPServer(("0.0.0.0", 8080), H).serve_forever()
python3 echo_headers.py

これは動作確認用です。上流のレスポンスを返さないので、Claude Codeからの推論は成功しません。ヘッダーの有無だけを見るためのものです。

メールが無いIdPトークンの扱い

IdPのトークンにメールが含まれないとき、ゲートウェイは x-claude-gateway-user-id だけを送ります。メールを載せる2つのヘッダーは付きません。

ADFSやEntra B2Cのように、メールを email ではなく upn や preferred_username に入れるIdPがあります。その場合は oidc.email_claim に実際のクレーム名を指定します。指定できるのは単純なキー名、JSON Pointer、フォールバック用のキーのリストで、リストでは最初に存在したキーが使われます。

oidc:
  email_claim: [email, preferred_username, upn]

email_claim はリストで書け、最初に存在したキーが使われます。自分のIdPがどのクレームでメールを出すかは、CLAUDE_GATEWAY_LOG_LEVEL=debug で各id_tokenのクレーム名をログに出して確認できます。

メールが取れない開発者は、プロキシ側から見ると x-claude-gateway-user-id しか持たない利用者になります。メールでの集計を前提にしていると、この利用者はメールで引けません。

プロキシの429はそのまま開発者に返る

ゲートウェイは通常、5xx、429、401、403、404、タイムアウトのときに次のupstreamへフェイルオーバーします。429 は本来、upstream単位の容量不足とみなされるためです。

forward_user_identity: true のupstreamでは、メールを載せたリクエストへの 429 だけ扱いが変わります。ゲートウェイはそれを開発者ごとの拒否とみなし、フェイルオーバーせずに応答をそのまま開発者へ返します。プロキシに置いた開発者ごとの予算やレート制限が、別のupstreamに迂回されずに効くようにするためです。

リクエストの状況プロキシが 429 を返したとき
メール付きで転送したプロキシが 429 を返したときフェイルオーバーせず、開発者へそのまま返す
メールが無く、IDだけ転送したプロキシが 429 を返したときupstreamの容量不足として、次のupstreamへフェイルオーバー
他のステータス(5xx など)プロキシが 429 を返したとき通常のフェイルオーバーの規則に従う

この 429 の扱いはゲートウェイがv2.1.267以降のときのものです。それより前は、すべての 429 がフェイルオーバーされました。v2.1.267は2026年9月9日のリリースです。

メールが無い開発者にだけ予算の上限が効かなくなる点は、運用で見落としやすい部分です。プロキシで予算を厳密に守りたいなら、全員のトークンにメールが入るよう email_claim を整えておく必要があります。

プロキシのエラー本文はそのまま画面に出る

provider: anthropic のupstreamが返すエラー本文は、ゲートウェイを通っても書き換えられず、そのまま開発者に届きます。Bedrockなどのクラウド系upstreamは、本文がゲートウェイ側で upstream rate limit exceeded のような定型文や capability_rejected: のトークンに置き換わります。自前プロキシは provider: anthropic なので、この置き換えを受けません。

つまり、プロキシが予算超過を知らせる文面を 429 の本文に入れておけば、その文面がClaude Codeの画面にそのまま表示されます。「予算は月初にリセットされます。追加が必要なら○○チャンネルへ」のように、次の行動まで書いておく使い方ができます。

429 のあとの扱いは、フェイルオーバーするかどうかで変わります。

  • 後ろのupstreamが無い、または全部が失敗した場合: 最後に返った 429 が開発者へ渡ります
  • 429 が一つも無く他の失敗だけだった場合: 401 / 403、404、501 の順で、最後に返ったものが優先されます
  • どれにも当てはまらない場合: ゲートウェイ自身の 502(all upstreams failed (N attempted))になります。Nは、モデルに対応しないため飛ばしたものも含めた upstreams の全件数です

また、429 以外の 4xx はフェイルオーバーの対象外で、そのupstreamの応答がそのまま返ります。リクエスト自体に原因があるエラーなので、別のupstreamに回しても結果は変わらないためです。

複数のupstreamと組み合わせるとき

ゲートウェイのupstreamは先頭から順に試されます。自前プロキシを先頭に置き、その後ろにBedrockなどを並べた構成では、プロキシが容量不足の 429 を返すと、通常は後ろのupstreamに回ります。forward_user_identity を付けると、メール付きのリクエストはこの迂回をしません。

プロキシが予算超過を 429 で返す設計なら、迂回しないほうが意図に合います。一方でプロキシ自身が容量で 429 を返す場合も同じく開発者に届くため、容量不足の際に後ろへ回したいなら、予算の拒否と容量の拒否を 429 以外で区別できるかをプロキシ側で検討してください。

起動拒否とメールの送信先

forward_user_identity は、開発者のメールを base_url の指すサーバーにそのまま送ります。そのため、base_url がAnthropic APIのまま(これが既定)のupstreamに付けると、ゲートウェイは起動を拒否します。

この拒否は誤設定への安全装置です。メールを外部のAPIに送らないという意味で、設定を足したときに最初に確認する項目になります。

自前のプロキシを指していても、そのプロキシの先にメールが渡るかどうかは別の問題です。プロキシがヘッダーをログに残す、あるいは上流に転送する構成なら、メールがそこまで届きます。この設定を入れるときは、プロキシのログ保存先とアクセス権も合わせて確認してください。

脅威モデルの観点では、ゲートウェイを通る情報のうちID情報だけは、この設定を有効にすると自社プロキシへ出ていく条件付きの項目になります。整理はClaude apps gatewayの脅威モデルとコンプライアンス対応にあります。

プロキシ経由だけの環境での通信

社内ネットワークの都合で、ゲートウェイの外向き通信をすべてフォワードプロキシに通す環境があります。provider: anthropic のupstreamは、この設定の有無で宛先の決まり方が変わります。

状態provider: anthropic upstreamへの接続
HTTPS_PROXY を設定しただけ(既定)provider: anthropic upstreamへの接続ゲートウェイがホスト名を自分で解決して確認し、確認したIPアドレスへプロキシ経由で CONNECT する
プロキシ専用の外向き通信が有効provider: anthropic upstreamへの接続ホスト名をそのままプロキシに渡し、解決はプロキシに任せる

プロキシ専用の外向き通信は、HTTPS_PROXY か HTTP_PROXY が設定され、NO_PROXY と no_proxy が空で、CLAUDE_GATEWAY_ALLOW_LOOPBACK が無効のときだけ有効になります。条件を満たさないと、ゲートウェイは起動時に警告を出して既定の動きのままにします。

forward_user_identity で渡したメールも、この経路を通ってプロキシへ届きます。社内の自前プロキシを base_url にしていて、さらにその手前にフォワードプロキシがある構成では、メールが通る箇所が増えます。ログを残す機器が増えていないか、経路ごとに確かめてください。詳しい設定はproxy経由だけの環境で動かす設定にあります。

固定ヘッダーとの使い分け

upstreamには、開発者ごとではなく固定のヘッダーを付ける headers: もあります。プロキシがヘッダーでルーティングや帰属を決めるときに使います。

項目forward_user_identityheaders:
値forward_user_identity開発者ごとに変わる(ID・メール)headers:設定ファイルに書いた固定値
必要なバージョンforward_user_identityゲートウェイv2.1.233以降headers:ゲートウェイv2.1.277以降
対象のproviderforward_user_identityprovider: anthropicheaders:すべてのprovider

headers: は古いゲートウェイだとキーを見つけた時点で起動を拒否します。全レプリカを先に上げてからキーを足し、ロールバックの前にはキーを消してください。

開発者ごとの帰属は forward_user_identity、環境名などの固定の識別は headers: と分けると、同じプロキシに対する設定が混ざりません。

予算をプロキシで持つか、ゲートウェイで持つか

開発者ごとの支出上限は、ゲートウェイ自身も持てます。admin: ブロックを設定すると、ゲートウェイがPostgresに支出を数え、上限を超えた開発者には 429 billing_error を返します。メッセージは blocked_message で追記できます。

両者の違いは、数える場所と止まり方です。

項目プロキシで数える(forward_user_identity)ゲートウェイで数える(admin:)
集計の場所プロキシで数える(forward_user_identity)自前プロキシ(LiteLLMなど)ゲートウェイで数える(admin:)ゲートウェイのPostgres
止める応答プロキシで数える(forward_user_identity)プロキシが返す 429(本文はそのまま届く)ゲートウェイで数える(admin:)ゲートウェイの 429 billing_error
メールが無い開発者プロキシで数える(forward_user_identity)429 が容量不足扱いになり、予算が効かないゲートウェイで数える(admin:)ゲートウェイ側の仕組みなので、この落とし穴とは別
ストア障害時プロキシで数える(forward_user_identity)プロキシの設計次第ゲートウェイで数える(admin:)既定は止めずに通す(fail_closed_on_error: true で止める)

プロキシが既に請求や社内の費用配賦の基盤になっているならプロキシ側、ゲートウェイだけで完結させたいならゲートウェイ側、という分け方が素直です。仕組みを二重にすると、どちらの 429 で止まったのかが分かりにくくなります。

設定を入れる順番

  1. ゲートウェイのバージョンを確かめる。429 の挙動まで揃えるなら、v2.1.267以降にする
  2. 確認用サーバーで、3つのヘッダーが期待どおり出ていることを見る
  3. メールが空の開発者がいれば、oidc.email_claim を直す
  4. 本番のプロキシに base_url を向け、forward_user_identity: true を有効にする
  5. プロキシ側で、開発者ごとの集計と予算の拒否が機能するかを見る

ゲートウェイ全体の設定項目はgateway.yamlリファレンスに、開発者ごとの支出上限をゲートウェイ自身で持つ方法は支出上限の解説にあります。プロキシで数える方式とゲートウェイで数える方式のどちらにするかは、その2本を見比べて決められます。

プロキシ経由だけの環境でゲートウェイを動かす場合の通信設定は、proxy経由だけの環境で動かす設定で扱っています。

この記事を共有:XはてブLinkedIn