Kong Gateway の request-transformer は、上流サービスへ渡す前のリクエストのヘッダー・クエリ文字列・ボディ・URI・メソッドを、設定した値どおりに書き換える同梱プラグインです。名前の似たものに、Enterprise 版の request-transformer-advanced と、LLM に書き換えさせる ai-request-transformer があります。この記事では OSS 版の最新タグ Kong Gateway 3.9.4(2026年10月12日公開)を Docker で動かし、設定の書き方と「設定したのに効かない」ときの原因を実測結果で示したうえで、3つのプラグインの選び分けと、PII マスキングに AI 版を使ってはいけない理由まで整理します。
まとめ
決まったルールでヘッダーやボディを書き換えるなら request-transformer を使います。外部通信を伴わず、同じ入力には同じ結果が返ります。書き換えが効かないときは、まず4点を疑ってください。add はクライアントが同名の値を送っていると何もしない、replace は既存のキーにしか効かない、テンプレートでハイフン入りのヘッダー名をドット記法で書くと 500 になる、replace.uri は Service に書いたパスごと置き換える。
ボディ内のキーを許可リストで絞る、ネストした JSON を書き換えるといった処理が要るなら Enterprise 版の Request Transformer Advanced を使います。ai-request-transformer は自然言語の指示で本文を作り変えたい場合に限ります。受け取った本文はそのまま LLM プロバイダへ送られるため、個人情報のマスキングには使えません。PII の除去には AI PII Sanitizer(Kong Gateway 3.10 以降・AI Gateway Enterprise)を使います。
request-transformer・Advanced・AI版の違い
OSS 版で使えるのは request-transformer と ai-request-transformer の2つで、どちらも Kong 3.9.4 の同梱プラグインです。
| 項目 | request-transformer | Advanced | ai-request-transformer |
|---|---|---|---|
| 提供 | OSS 同梱 | Enterprise のみ | OSS 同梱(3.6 以降) |
| 書き換えの指示 | 設定値とテンプレート | 設定値とテンプレート | 自然言語プロンプト |
| 書き換え対象 | URI・メソッド・ヘッダー・クエリ・ボディ | 同左+ボディの許可リスト | ボディのみ |
| ボディの Content-Type | JSON・フォーム・multipart | JSON のみ | 問わない |
| 外部への通信 | なし | なし | LLM API へ1回 |
| 実行優先度 | 801 | 802 | 777 |
| コンシューマー単位 | 可 | – | 不可 |
優先度は各プラグインの PRIORITY の値で、大きい方が先に動きます。同じルートに両方を載せると、request-transformer が書き換えた本文が ai-request-transformer に渡ります。OSS のタグは 3.9.4 が最新で、3.10 以降は Enterprise 版としてだけ配布されています。3.9.3 から 3.9.4 への変更は hybrid モードの SQL インジェクション修正などで、どちらのプラグインのファイルにも差分はありません(GitHub の 3.9.3 と 3.9.4 の比較)。Kong Gateway の Service と Route の作り方はKong GatewayのUpstream設定と負荷分散:Service・Routeの作り方を3.9.4で実測で扱っています。
request-transformer の設定を書く
設定は remove・rename・replace・add・append の5つの操作ごとに、headers・querystring・body の配列を書きます。replace にだけ uri があり、別項目の http_method でメソッドも変えられます。
5つの操作と実行順
公式リファレンスが示す実行順は remove → rename → replace → add → append です(Request Transformer の公式リファレンス)。順序が決まっているので、同じ名前を複数の操作に書いたときの結果は予測できます。X-Old を X-New へ rename し、同じ設定で add に X-Old:fresh を書くと、上流には X-New: orig と X-Old: fresh の両方が届きました。rename で名前が空いたあとに add が動くためです。
| 操作 | 書式 | 同名がすでにあるとき |
|---|---|---|
| remove | 名前だけ | 削除する |
| rename | 旧名:新名 | 新名へ移す |
| replace | 名前:値 | 値を差し替える(無ければ何もしない) |
| add | 名前:値 | 何もしない(無ければ追加) |
| append | 名前:値 | 値を追加して複数にする |
append で X-Multi:from-kong を足すと、クライアントが送った X-Multi: from-client と並んで2つの値が上流に届きます。値にカンマを含めたいときは、カンマ区切りの書き方ではなく配列で書きます。
ボディを書き換えられる Content-Type
ボディの書き換えは Content-Type が application/json・application/x-www-form-urlencoded・multipart/form-data のときだけ動きます。access.lua がこの3種類しか判定しておらず、text/plain で user=a&password=p を送ったときは remove も add も効かず、本文はそのまま上流へ届きました。JSON で送ると password が消えて source が足され、フォームと multipart でも同じ結果でした。
URI・メソッド・クエリ文字列の書き換え
次のプラグイン設定例は、Service の URL を http://httpbin:8080/anything、Route の paths を /t5、strip_path を true とした環境で、GET を POST に変え、クエリ q を keyword へ改名し、lang=ja を足します。/t5?q=kong への GET は、上流で POST /anything?keyword=kong&lang=ja になりました。
plugins:
- name: request-transformer
config:
http_method: POST
rename:
querystring: ["q:keyword"]
add:
querystring: ["lang:ja"]
テンプレート変数で値を組み立てる
値の部分には $(...) の形でテンプレートを書けます。参照できるのは headers・query_params・uri_captures(Route の正規表現の名前付きキャプチャ)・shared(他のプラグインが kong.ctx.shared に入れた値)の4つです。
ハイフン入りのヘッダー名は角括弧で書く
$(headers.x-secret) と書いた設定は、宣言型設定の読み込み(POST /config)では 201 で受理されます。ところがリクエストを送ると 500「An unexpected error occurred」が返り、エラーログには次の行が出ました。
[request-transformer] failed to render the template $(headers.x-secret), error:[string "TMP"]:1: attempt to perform arithmetic on field 'x' (a nil value)
$(...) の中身は Lua の式として評価されるため、headers.x-secret が「headers.x から secret を引く」と解釈されています。設定時には検出されず、そのルートへ最初にリクエストが来た時点で壊れる点に注意してください。$(headers["x-secret"]) と角括弧で書くか、$(headers.x_secret) とハイフンをアンダースコアに置き換えれば、どちらも値 s3cr3t を取り出せました。
テンプレートの参照時点と変換前の値
テンプレートは変換が始まる前のリクエストを読みます。次の設定で X-Secret: s3cr3t を送ると、X-Secret は上流に届かず、X-Copied: s3cr3t が届きました。一方、同じ設定で add した X-A を X-B から参照すると、既定値の none が入りました。remove した値は後続のテンプレートから読めて、add した値は読めません。
config:
remove:
headers: ["X-Secret"]
add:
headers: ["X-Copied:$(headers['x-secret'])", "X-A:added", "X-B:$(headers['x-a'] or 'none')"]
消したはずの値がテンプレート経由で上流へ出ていくことがあるので、remove と同じ名前をテンプレートで参照していないかは見直してください。
Lua の式で既定値や加工を書く
式なので or で既定値を書けます。$(query_params.tier or 'standard') は ?tier=gold なら gold、無ければ standard を返しました。加工には文字列のメソッドを使います。$((query_params.tier or 'standard'):upper()) は GOLD を返しましたが、string.upper('abc') のように string を直接呼ぶと「attempt to index global ‘string’ (a nil value)」で 500 になりました。公式リファレンスのとおり、サンドボックスの中では文字列のメソッド以外のライブラリ関数を使えません。kong.conf の untrusted_lua を off にした環境では、Lua の式そのものが使えなくなります。
変換が効かないときの原因と対処
Kong 3.9.4 の実測で確かめた原因です。どれも設定の読み込み時にはエラーになりません。
| 症状 | 原因 | 対処 |
|---|---|---|
| add したヘッダーが届かない | クライアントが同名を送信済み | replace か remove と add の併用 |
| replace が効かない | そのキーが元のリクエストに無い | add を併記する |
| 500 が返る | テンプレートの書き方 | 角括弧の記法にする |
| replace.uri 後に 404 | Service のパスも置き換わる | uri に Service のパスを含める |
| X-Forwarded-For が変わらない | Kong が後から付け直す | Post-Function などで設定 |
| ボディが変わらない | Content-Type が対象外 | JSON かフォームで送る |
| 上流のパスが想定と違う | strip_path の既定が true | Route で strip_path を明示 |
1行目は、既存のヘッダーを上書きできない例です。add.headers に X-Env:prod を書き、クライアントが X-Env: dev を送ると、上流には dev のまま届きました。Kong のナレッジベースも同じ挙動を説明し、上書きには replace.headers か remove と add の組み合わせを勧めています(Kong のナレッジベース記事)。ただし replace は元のリクエストにキーがあるときしか動かないため、「あれば上書き、無ければ追加」にしたいなら remove と add を両方書くのが確実です。
4行目は、Service の URL を http://httpbin:8080/anything にした状態で replace.uri に /v2/users/$(uri_captures.uid) を書いたときに起きました。上流へのパスは /anything/v2/users/42 ではなく /v2/users/42 になり、404 が返ります。replace.uri は上流へのパス全体を置き換えるので、Service 側のパスも含めて書いてください。
X-Forwarded-For は replace に 10.0.0.1 を書いても、上流には 1.2.3.4, 172.22.0.1(クライアントが送った値に Kong が受けた接続元を足したもの)が届きました。X-Forwarded-Host の add も効きません。公式リファレンスも X-Forwarded-* はこのプラグインで上書きできないと明記し、Post-Function プラグインを案内しています。
Enterprise 版の Kong Gateway を使っている場合は、版ごとの修正も見てください。3.14.0.0 で add.headers がヘッダー名を小文字にしてしまう不具合が、3.15.0.1 と 3.16.0.0 で remove.headers に 名前:値 の形で書くと削除されない回帰が直っています(request-transformer の変更履歴)。
Request Transformer Advanced で増える機能
Request Transformer Advanced(request-transformer-advanced)は Enterprise のみのプラグインで、OSS 版では使えません。標準版に無い機能として公式リファレンスが説明しているのは次の2つです(Request Transformer Advanced の公式リファレンス)。
config.allow.bodyで、ボディに残すキーを許可リストで指定できる(リストに無いキーは上流へ渡らない)config.remove.jsonにcustomers[*].info.phoneと書くように、ネストした JSON や配列をドット記法とワイルドカードで指定できる(配列の添字は1から。この記法が効くのはconfig.dots_in_keysを false にしたときで、既定は true)
注意したいのはボディの対象です。Advanced のボディ変換は Content-Type が application/json のリクエストだけで、XML は対象外と FAQ に書かれています。標準版がフォームと multipart のボディも書き換えるのとは逆なので、フォーム送信を受ける API を Advanced へ移すと、ボディの書き換えが効かなくなります。公式の手順例は、クエリの customer_id をボディへ移し、許可リストにない customer_phone を落とす構成です(Transform a client request in Kong Gateway)。上流に渡したくない項目を確実に落としたいなら、標準版の remove で列挙するより、Advanced の許可リストの方が項目の追加漏れに強い設計です。
ai-request-transformer の設定と既定値
ai-request-transformer は、設定したプロンプトを system メッセージ、受け取ったリクエストの本文を user メッセージとして LLM に送り、LLM の応答をそのまま上流へのボディにします(AI Request Transformer の公式リファレンス)。
必須の設定と最小構成
必須は config.prompt と config.llm 配下の route_type・model.provider です。prompt を省くと宣言型設定の読み込みが「required field missing」で拒否され、route_type に llm/v1/completions を書くと「’config.llm.route_type’ must be ‘llm/v1/chat’ for AI transformer plugins」で拒否されました。プラグインのスキーマが consumer を受け付けないので、コンシューマー単位では設定できません。
plugins:
- name: ai-request-transformer
config:
prompt: "Rewrite the description field in the JSON in plain language and return JSON only."
llm:
route_type: llm/v1/chat
auth:
header_name: Authorization
header_value: Bearer <APIキー>
model:
provider: openai
name: gpt-5.6
社内の LLM やテスト用のモックへ向けるときは、model.options.upstream_url に完全な URL を書きます。今回の実測では、OpenAI 互換の応答を返すモックサーバーをこの項目で指定しました。
LLM の応答が本文になるまでと失敗時の応答
モックに届いたリクエストを記録すると、messages は system(プロンプト)と user(クライアントが送った本文そのまま)の2件で、stream は false でした。上流へは LLM の応答文字列が本文として渡り、Content-Type はクライアントが送ったものが残ります。失敗時の応答は次のとおりです。
| 条件 | クライアントへの応答 |
|---|---|
| LLM が 500 を返した | 400 failed to introspect request with AI service: status code 500 |
| 抽出パターンに一致しない | 400 AI response did not match specified regular expression |
| LLM が JSON 以外を返した | 上流がそのまま受け取る |
3行目が見落としやすい挙動です。LLM が「Here you go:」のような前置きを付けて JSON を返すと、Kong はその文字列を Content-Type application/json のまま上流へ渡し、上流の httpbin が JSON の解析エラーで 400 を返しました。transformation_extract_pattern に \{.*\} を書くと、応答から JSON 部分だけを取り出して上流へ渡せます。公式リファレンスも、AI Proxy と連鎖させたときにモデルが厳密な JSON を返さないと失敗することがある、と既知の障害として挙げています。
本文サイズの上限 max_request_body_size
Kong 3.9.4 の既定値は 8192 バイトです(現行の公式リファレンスでは 1048576)。ただし、この値で本文が打ち切られるわけではありません。実測では 9,000 バイトや 10,000 バイトの本文は全文が LLM に届きました。上限が効くのは、本文が Nginx のバッファ(client_body_buffer_size 8k)に収まらず一時ファイルへ退避されたときだけです。
20,000 バイトの本文を送ると、Nginx が「a client request body is buffered to a temporary file」を記録したあと、content の無い user メッセージで LLM が呼ばれました。Kong は本文を読めなかったことをエラーにせず、モックの応答をそのまま上流へ渡して 200 を返しています(今回の環境では 12,000 バイトから同じ結果)。filters/transform-request.lua が kong.request.get_raw_body() のエラーを確かめずに LLM 呼び出しへ進むためです。実際のプロバイダが空の user メッセージを拒否すれば 400 になり、受け付ければプロンプトだけから作られた本文が上流へ渡ります。max_request_body_size を 1048576 にすると、20,000 バイトの本文も全文が届きました。大きな本文を扱うルートでは、この値を必ず明示してください。
そのほかの既定値は、LLM の応答待ちの上限 http_timeout が 60000 ミリ秒、証明書検証の https_verify が true です。
PII マスキングに AI 版を使わない理由と AI PII Sanitizer
「AI プラグインでマスキングすれば外部に漏れない」という設計は成り立ちません。前章の実測でモックに届いた user メッセージには、マスキング前の {"name":"山田太郎","email":"[email protected]","plan":"free"} がそのまま入っていました。マスキングされるのは LLM の応答を受け取った後、つまり上流サービスへ渡る内容だけで、OpenAI などのプロバイダには元の値が届きます。PII(個人を特定できる情報)とは?読み方・piiデータの具体例と個人情報との違いで扱う範囲のデータを、保護するつもりで第三者に渡すことになります。加えて、伏せ字になったかを Kong 側で検証する仕組みはなく、LLM の障害がそのまま API の 400 になります。
AI PII Sanitizer の構成と既定値
Kong が PII 除去用に用意しているのは AI PII Sanitizer(ai-sanitizer)です。Kong Gateway 3.10 以降で、AI Gateway Enterprise に含まれます。NLP モデルを内蔵した AI PII Anonymizer サービス(Docker イメージ kong/ai-pii-service:v0.2.2-en など)を別に動かし、プラグインがそこへ問い合わせます。イメージは Kong のプライベートリポジトリで配布され、言語別のタグには日本語の -ja もあります(AI PII Sanitizer の公式リファレンス)。
| 設定 | 既定値 | 意味 |
|---|---|---|
| anonymize | all_and_credentials | 匿名化する種類 |
| redact_type | placeholder | synthetic で同種の別語に置換 |
| sanitization_mode | INPUT | OUTPUT・BOTH も可 |
| block_if_detected | false | true で検出時に遮断 |
| recover_redacted | true | 応答で元の値に戻す |
| host・port | localhost・8080 | PII サービスの接続先 |
| stop_on_error | true | PII サービス障害時に止める |
AI 版との違いは処理の順序です。AI PII Sanitizer は AI Proxy または AI Proxy Advanced と組み合わせ、その転送先の LLM に送る前に匿名化します。ただし、既定の実行優先度では AI Request Transformer が先に動くため、同じルートに追加するだけでは変換用 LLM への未加工データ送信を防げません。応答側の匿名化は 3.12 以降で使えます。OSS 版だけで PII を守るなら、上流アプリケーションで決定的にマスキングするか、request-transformer の remove で項目ごと落とすのが現実的な選択です。ゲートウェイにどこまで責務を持たせるかはAPIゲートウェイとは?役割・機能とリバースプロキシ/サービスメッシュとの違い・導入判断を解説で整理しています。
ローカルで変換結果を確かめる
上流に go-httpbin を置くと、変換後のリクエストを JSON で目視できます。/anything が受け取ったメソッド・URL・ヘッダー・本文を返すためです。再現するには、まず後掲の YAML を作業ディレクトリの kong.yaml に保存してから、次のコマンドを実行します(DB-less モード)。
docker network create kongnet
docker run -d --name httpbin --network kongnet mccutchen/go-httpbin:latest
docker run -d --name kong --network kongnet \
-v "$PWD":/w -e KONG_DATABASE=off \
-e KONG_DECLARATIVE_CONFIG=/w/kong.yaml \
-e KONG_ADMIN_LISTEN=0.0.0.0:8001 \
-p 127.0.0.1:8000:8000 -p 127.0.0.1:8001:8001 kong/kong:3.9.4
kong.yaml には Service とルートとプラグインを書きます。Service の URL にパスを含めておくと、strip_path の既定値 true でルートの /t1 が取り除かれ、上流には /anything が届きます。
_format_version: "3.0"
services:
- name: hb
url: http://httpbin:8080/anything
routes:
- name: t1
paths: ["/t1"]
plugins:
- name: request-transformer
config:
add:
headers: ["X-Env:prod"]
設定を書き換えたら、コンテナを作り直さずに Admin API へ送り直せます。DB-less モードでは deck gateway sync や Admin API のエンティティ作成・更新用 POST・PATCH は使えないので、/config へファイルごと送ります。
curl -s -X POST http://127.0.0.1:8001/config -F [email protected]
curl -s http://127.0.0.1:8000/t1
curl -s http://127.0.0.1:8000/t1 -H 'X-Env: dev'
2つ目の curl では応答の headers に X-Env: prod が入り、3つ目では dev のままになります。ボディの変換は応答の data(生の文字列)と json(解析済み)で確かめます。500 が返ったら docker logs kong で「failed to render the template」を探すと、テンプレートのどこで失敗したかが分かります。
Kong を含む API 基盤の設計や既存システムとの連携の実装は、API開発・システム連携でご相談いただけます。
よくある質問
request-transformer と ai-request-transformer はどちらを使うべきですか?
ヘッダーの追加や項目の削除のように結果が決まっている変換は request-transformer を使います。ai-request-transformer は LLM API を1回呼ぶぶん遅延と費用がかかり、同じ入力でも結果が変わります。自然言語の指示でしか書けない変換に限って使ってください。
request transformer advanced との違いは何ですか?
Advanced は Enterprise 版だけのプラグインで、ボディの許可リスト(allow.body)とネストした JSON の書き換えが加わります。一方でボディ変換は JSON だけが対象で、標準版が扱えるフォームと multipart は書き換えられません。
Kong の PII Sanitizer は OSS 版で使えますか?
使えません。AI PII Sanitizer は Kong Gateway 3.10 以降の AI Gateway Enterprise に含まれるプラグインで、OSS のタグは 3.9.4 までです。PII サービスのイメージもプライベートリポジトリで配布されています。
クライアントが送ったヘッダーを上書きできないのはなぜですか?
add は同名のヘッダーがすでにあると何もしないためです。replace を使うか、remove と add に同じ名前を書きます。X-Forwarded-For などの X-Forwarded-* は Kong が後から付け直すので、このプラグインでは変えられません。
ai-request-transformer は OSS 版の Kong Gateway でも使えますか?
使えます。Kong 3.9.4 の同梱プラグイン一覧に含まれ、ライセンス無しで起動したコンテナでも設定・実行できました。LLM プロバイダの API キーは別途必要です。