Kong Gateway でリクエストを書き換えるプラグインには、設定した値どおりに決定論的な書き換えを行う request-transformer と、リクエストボディを LLM へ渡して自然言語の指示で書き換えさせる ai-request-transformer の2つがあります。名前が似ているうえ設定項目がまったく異なるため、公式リファレンスを片方だけ見て取り違える事故が起きやすい組み合わせです。この記事では両プラグインの設定項目・既定値・実行順を、OSS 版の最新タグである Kong Gateway 3.9.3(2026年6月17日公開)のソースコードで確認した内容にもとづいて整理し、PII マスキングに使うべきプラグインまで示します。
まとめ
ヘッダー・クエリ文字列・ボディを決まったルールで書き換えるなら request-transformer を選びます。外部通信を伴わないので結果は常に同じ。公式リファレンスが対応を明記する最小版は Kong Gateway 1.2 です。リクエスト内容を自然言語の指示で作り変えたい場合だけ ai-request-transformer(Kong Gateway 3.6 以降)の出番になります。必ず書く項目は config.prompt と config.llm(配下の route_type と model.provider が必須のため実質必須)の2つ。config.llm.route_type に llm/v1/chat 以外を設定するとスキーマ検証で拒否されます。
個人情報のマスキングを目的に ai-request-transformer を選ぶ設計は誤りです。このプラグインはクライアントから受け取った本文を加工せず user メッセージとして LLM に送信するため、マスキングされるのは上流サービスへ渡る内容だけで、LLM プロバイダには生データが届きます。PII の検出と除去には専用の AI PII Sanitizer プラグイン(Kong Gateway 3.10 以降・AI Gateway Enterprise)を使ってください。以降では設定項目と、既定値のまま運用したときに詰まる箇所を順に見ていきます。
2つのリクエスト変換プラグインの違い
両者は「リクエストを書き換える」という説明文こそ似ていますが、変換の指示方法・外部通信の有無・書き換え対象が異なります。Kong Gateway 3.9.3 のソースで確認した差分は次のとおりです。
| 項目 | request-transformer | ai-request-transformer |
|---|---|---|
| 公式が明記する最小版 | Kong Gateway 1.2 | Kong Gateway 3.6 |
| 変換の指示 | 設定値とテンプレート | 自然言語プロンプト |
| 書き換え対象 | URI・メソッド・ヘッダー・ボディ・クエリ | ボディのみ |
| 外部への通信 | なし | LLM API へ1回 |
| 結果の再現性 | 決定的 | 非決定的 |
| 実行優先度 | 801 | 777 |
| コンシューマー単位の適用 | 可 | 不可 |
実行優先度は数値が大きいほど先に走ります。両方を同じルートに設定した場合は request-transformer(801)が先、ai-request-transformer(777)が後。決定論的な整形を先に済ませてから LLM へ渡す並びが、既定のまま自然に成立します。なお Kong Gateway 3.0 以降の Enterprise 版にはプラグイン順序制御(ordering.before / ordering.after)があり、アクセスフェーズに限ってこの既定順を上書きできます。優先度の数値だけで実行順を断定はできません。またコンシューマー単位の適用可否が分かれるのは、ai-request-transformer のスキーマが consumer = typedefs.no_consumer を宣言しているからで、こちらはグローバル・サービス・ルートのいずれかにしか付けられません。Kong Gateway 自体の Service と Route の関係はKong Gatewayとは?UpstreamとService/Routeで理解するAPIゲートウェイの設定で整理しています。
request-transformer の設定項目と書式
request-transformer の config は remove・rename・replace・add・append の5操作と http_method だけ。5操作はそれぞれ headers・querystring・body の3つを持ち、replace のみ uri も指定できます。http_method は上流へ送る HTTP メソッドを大文字で固定する項目です。GET・HEAD・TRACE のいずれかに変え、かつ元のリクエストが application/x-www-form-urlencoded だった場合には、ボディの各パラメータがクエリ文字列へ移されます。
操作ごとの指定形式と適用順
指定形式は操作によって変わります。remove は削除対象の名前だけを並べる形式。rename は変更前と変更後を、replace・add・append はキーと値をコロンで区切った1行で書きます。コロンを含まない文字列を add に渡すと、スキーマ検証の時点で弾かれます。なお以下は decK 形式で、Admin API から config.replace.body=email:REDACTED のようにフォーム形式で渡しても設定内容は同じです。
_format_version: "3.0"
services:
- name: httpbin
url: http://httpbin.org
routes:
- name: anything
strip_path: false
paths:
- /anything
plugins:
- name: request-transformer
config:
remove:
headers:
- authorization
rename:
headers:
- x-old-name:x-new-name
replace:
body:
- email:REDACTED
add:
querystring:
- source:gateway
append:
headers:
- x-forwarded-extra:kong
ルートに strip_path: false を明示している点に注意してください。この項目の既定は true で、指定を省くと /anything というマッチしたプレフィックスが剥がされ、上流には http://httpbin.org/ が要求されます。結果として返るのはエコー用の JSON ではなく httpbin のトップページ(HTML)です。検証がうまくいかない原因の多くはここにあります。
適用順は設定ファイルの記述順ではなく実装で固定されています。リクエスト全体では URI、HTTP メソッド、ヘッダー、ボディ、クエリ文字列の順に処理され、ヘッダーの中ではさらに remove、rename、replace、add、append の順に適用。同じヘッダーを remove と add の両方に書いた場合は削除が先に走るため、最終的に残るのは add の値になります。
テンプレート変数による動的な値の埋め込み
値の側にはテンプレートを書けます。参照できるのは headers、query_params、uri_captures、shared の4つで、書式は $(...)。uri_captures はルートの正規表現パスで捕捉したグループ、shared は同一リクエスト内で先に走ったプラグインが kong.ctx.shared に書いた値を指します。中身は Lua の式として評価されるため、or によるフォールバックも書けます。
config:
add:
headers:
- x-original-host:$(headers.host)
- x-tenant:$(uri_captures.tenant or query_params.tenant or "unknown")
querystring:
- trace:$(query_params.request_id)
ただしこのテンプレート環境は読み取り専用で、各変数は最初に参照された時点の値がキャッシュされます。参照できるのは常に変換前のスナップショットだ、ということです。ここから2つの帰結が出ます。remove で消したはずのヘッダーもスナップショットには残っているので、後続の add のテンプレートから読めてしまいます。逆に add や append で足した値は、後続のテンプレートからは参照できません。より複雑な条件分岐や正規表現による抽出が必要な要件には、Enterprise 版の request-transformer-advanced が別プラグインとして用意されています。OSS 版の request-transformer にリクエスト内容による条件分岐の機能は無いので、条件で変換を切り替えたいならルート設計側で分けます。
ai-request-transformer の必須設定と既定値
こちらのスキーマには mask_fields や add_fields のようなフィールド指定の項目がありません。何をどう書き換えるかは、すべて config.prompt の文章で指示します。
config.prompt と config.llm の最小構成
既定値を持たず利用者が必ず書かなければならないのは config.prompt(文字列)だけです。config.llm フィールド自体には required が付いていませんが、配下の route_type と model.provider が必須で既定値も無いため、ブロックごと省略はできません。model.provider に指定できるのは Kong Gateway 3.9.3 時点で openai、azure、anthropic、cohere、mistral、llama2、gemini、bedrock、huggingface の9種類。認証情報は auth にヘッダー形式(header_name と header_value)またはパラメータ形式(param_name・param_value・param_location)で渡します。
_format_version: "3.0"
services:
- name: httpbin
url: http://httpbin.org
routes:
- name: anything
strip_path: false
paths:
- /anything
plugins:
- name: ai-request-transformer
config:
prompt: "Rewrite the JSON body so that the values of the keys email and phone are replaced with the string REDACTED. Return only the JSON object."
transformation_extract_pattern: "\\{[\\s\\S]*\\}"
llm:
route_type: llm/v1/chat
auth:
header_name: Authorization
header_value: Bearer sk-xxxxxxxxxxxxxxxx
model:
provider: openai
name: gpt-5.6
model.name はプロバイダへそのまま転送されるだけで、Kong 側の検証を受けません(スキーマ上も required = false で候補値の制約なし)。モデルの世代交代は速いので、利用時点で提供中のモデル名を公式のモデル一覧で確認してから設定してください。
model.options をあえて省いている点にも理由があります。options に書いた temperature・max_tokens・top_p・top_k は、Kong がプロバイダへ送るペイロードへそのまま載せる実装です。モデル側が受け付けないパラメータを書けば、deck の検証は通るのにプロキシ実行時だけ失敗します。まず省略して疎通させ、必要になってから足すのが安全な順序です。transformation_extract_pattern の正規表現で . ではなく [\s\S] を使っているのも同じ実務上の理由で、Kong は照合フラグに ijom を指定しており dotall が含まれないため、. は改行にマッチしません。整形された複数行の JSON を取り出すには [\s\S] が要ります。
プラグイン内部では、config.prompt が system メッセージ、クライアントが送ってきた生の本文が user メッセージとして組み立てられ、stream を false にして LLM へ送られます。返ってきた choices[0].message.content の文字列が、そのまま上流へのリクエストボディに差し替わる仕組みです。つまり LLM が説明文を付けて返せば、その説明文ごと上流へ流れてしまいます。transformation_extract_pattern はこの対策で、指定した正規表現の最初のマッチだけを本文として採用するもの。マッチしなければ変換は失敗扱いとなり、クライアントには 400 が返ります。なお provider が bedrock で、かつ system ロールに対応しないモデル名の場合だけは、user(プロンプト)・assistant・user(本文)の3メッセージに組み替えられます。
route_type が llm/v1/chat に固定される理由
config.llm.route_type のスキーマ自体は、3.9.3 では llm/v1/chat・llm/v1/completions・preserve の3値を受け付けます(現行の公式リファレンスでは音声・画像・埋め込み向けの値も加わり、受理値が増えています)。しかし ai-request-transformer 側の entity_checks が llm/v1/chat 以外を拒否するため、実際に設定できる値は1つだけ。エラーメッセージは「’config.llm.route_type’ must be ‘llm/v1/chat’ for AI transformer plugins」で、deck apply や Admin API への POST の時点で失敗します。system と user の2メッセージを組み立てる実装である以上、チャット形式以外は成立しないためです。OpenAI 側のエンドポイント選択についてはOpenAI Responses APIとは|Chat Completions APIとの違いと移行判断で整理しています。
既定値のまま本番投入で詰まる3項目
| 設定 | 3.9.3 の既定値 | 詰まり方 |
|---|---|---|
| max_request_body_size | 8192 バイト | ディスク退避時に取得失敗 |
| http_timeout | 60000 ミリ秒 | 応答待ちで滞留 |
| https_verify | true | 自己署名証明書で失敗 |
max_request_body_size は名前から受ける印象と挙動が違うので注意が必要です。プラグインは kong.request.get_raw_body() にこの値を渡しますが、PDK 側でこの引数が参照されるのは、本文が client_body_buffer_size(Kong の既定は 8k)に収まらず一時ファイルへ退避されたときだけ。バッファに収まる本文ではこの上限は一切参照されず、8 KiB を超えていてもそのまま LLM へ送られます。逆にディスクへ退避したうえで上限を超えた場合は本文の取得自体が失敗し、切り詰められた本文が送られることはありません。上限を引き上げると、それまで失敗していた大きな本文が実際にファイルシステムから読み出されるようになるので、性能への影響もあわせて見ておいてください。
http_timeout の 60 秒は、LLM の応答を待つあいだクライアントを待たせ続ける時間です。超過すると「failed to introspect request with AI service」を含む 400 が返ります。ローカルや社内に立てた LLM を model.options.upstream_url で指す構成なら、https_verify を false にするかどうかの判断も要ります。
PIIマスキング目的で ai-request-transformer を選ぶべきでない理由
「AI プラグインでマスキングすれば機密情報が外部に漏れない」という設計は成立しません。理由はいずれも実装上の性質に根ざしており、設定では回避できないものです。
LLM プロバイダへ届く生のリクエストボディ
プラグインは kong.request.get_raw_body() で取得した本文を加工せず user メッセージへ入れ、LLM プロバイダへ送信します。マスキングが起きるのは、LLM の応答を上流サービスへ渡す段階。したがってクレジットカード番号やマイナンバーを含むリクエストにこのプラグインを当てると、上流アプリケーションからは値が隠れる一方で、OpenAI や Anthropic には元の値が届きます。守りたい相手が外部 SaaS であるなら、この構成は保護対象を1つ増やしているにすぎません。PII(個人を特定できる情報)とは?piiデータの定義・種類と個人情報の違いで扱う範囲のデータを、意図せず第三者へ渡すことになります。
マスキング成否を検証する手段の不在
マスキングを実行するのは LLM であり、Kong 側には結果を検証する処理がありません。transformation_extract_pattern は「正規表現にマッチする文字列を取り出す」だけで、指定フィールドが伏せ字になったことを確認するものではないからです。model.options.temperature を下げても同一プロンプトで同一出力が返る保証はプロバイダ側になく、そもそもこのパラメータを受け付けないモデルもあるため、監査対応の要件を満たせません。加えて失敗時の挙動は、クライアントへの 400 応答(LLM 呼び出しの失敗)または 500 応答(ドライバ初期化・認証の失敗)です。フェイルオープンではなくリクエスト自体が通らなくなるため、LLM プロバイダの障害がそのまま API の障害になります。常時稼働の経路に置く設計としては割に合いません。
PII 除去に使う AI PII Sanitizer との違い
Kong は PII 除去のために別プラグイン AI PII Sanitizer(ai-sanitizer)を用意しています。対象は Kong Gateway 3.10 以降・AI Gateway Enterprise で、NLP モデルを内蔵した AI PII Anonymizer サービス(Docker イメージ docker.cloudsmith.io/kong/ai-pii/service:v0.1.2-en)を別プロセスとして動かし、既定では localhost の 8080 番へ接続する構成。決定的な違いは処理の順序で、こちらはリクエストが LLM プロバイダへ到達する前に匿名化を済ませます。block_if_detected(既定 false)を有効にすれば、匿名化ではなく PII を検出した時点でリクエストを遮断できます。伏せ字を応答側で元に戻す recover_redacted は既定で有効です。
ただしこの Docker イメージは Cloudsmith のプライベートレジストリで配布されており、取得には Kong サポートへの申請が必要になります。OSS 版の Kong Gateway だけで完結する要件ではありません。PII マスキングを本気で実装するなら、AI Gateway Enterprise を導入するか、アプリケーション側あるいは独自プラグインで決定論的にマスキングするかの二択です。ai-request-transformer で代替する選択肢は、上に挙げた理由からここには入りません。API ゲートウェイにどこまでの責務を持たせるかという設計判断はAPIゲートウェイとは?役割・機能とリバースプロキシ/サービスメッシュとの違い・導入判断を解説で整理しています。
httpbin.org で変換結果を確認する手順
どちらのプラグインも、上流に httpbin.org を置くと変換後のリクエストをそのまま目視できます。httpbin.org の /anything は受け取ったヘッダー・クエリ・ボディを JSON で返すため、上流アプリケーションを用意せずに検証できるからです。
前掲の2つの設定は同じサービス名・ルート名・パスを使っているので、同時には適用できません。切り替えるときは deck gateway sync を使います。deck gateway apply は加算専用でファイルに書いていないエンティティを削除しないため、2つ目を apply すると同じルートに両方のプラグインが載り、優先度どおり 801 の request-transformer が変換した本文が 777 の ai-request-transformer へ渡ってしまいます。
deck gateway sync kong.yaml
curl -i -X POST http://localhost:8000/anything \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer dummy' \
-d '{"email":"[email protected]","phone":"090-1234-5678"}'
上流へ実際に渡ったボディは、応答 JSON の data(生の文字列)と json(パース済みのオブジェクト)の両方に入ります。JSON を送っているなら json の方が読みやすいはずです。request-transformer の設定では email の値が REDACTED に置き換わる一方、phone はそのまま残ります。replace が既存のキーにだけ値を差し替える実装だからで、この差も同時に確認できるわけです。ほかに add したクエリ文字列は args に出て、remove を指定した authorization ヘッダーは headers から消えています。ai-request-transformer の設定であれば、data には LLM が返した文字列がそのまま入ります。
HTML が返ってきたときは strip_path の設定漏れです。400 が返る場合、原因は応答ボディの error.message に入っています。LLM 呼び出し自体の失敗なら「failed to introspect request with AI service」、正規表現が一致しなかった場合は「AI response did not match specified regular expression」という文言です。同じ文言は kong.log.info でも記録されるものの、Kong の既定ログレベルは notice なので error.log には出力されません。ログを追う前に、まず curl の応答ボディを見てください。
ひとつ例外があります。本文が大きすぎて取得に失敗したケースです。プラグインは kong.request.get_raw_body() の戻り値を引数の位置でそのまま渡しているため、PDK が返すエラー文字列(「request body file too big」)は捨てられます。この文言は応答ボディにもログにも出ません。本文だけが空のまま LLM へ渡り、プロバイダ側のエラーとして 400 が返るので、心当たりがあればボディサイズを疑ってください。
なお ai-request-transformer は LLM の利用状況をログに出せるものの、config.llm.logging.log_statistics と config.llm.logging.log_payloads はいずれも既定 false です。トークン消費量を追うなら明示的に有効化します。log_payloads はリクエスト本文をログへ書き出すため、機密データを扱う経路では有効化の前にログの保管先を必ず確認してください。Kong 以外のゲートウェイでリクエスト変換やログの責務をどう分担するかは、AWS CloudFront、API Gateway、Lambdaの連携とは何か?基本解説で扱っています。
よくある質問
request-transformer と ai-request-transformer はどちらを使うべきですか?
書き換えのルールを事前に定義できるなら request-transformer です。外部通信が発生せず結果が決定的で、ヘッダー・クエリ・URI・メソッドまで扱えます。ai-request-transformer が要るのは、自由記述のテキストを要約・翻訳・構造化するなど、ルールとして書き下せない変換だけに限られます。
ai-request-transformer は OSS 版の Kong Gateway でも使えますか?
使えます。Kong Gateway 3.9.3 の kong/constants.lua にある同梱プラグイン一覧に ai-request-transformer が含まれており、ハンドラーとスキーマも OSS リポジトリ上のものです。ただし LLM プロバイダの API 利用料は別途かかります。
ai-request-transformer で個人情報をマスキングできますか?
上流サービスに対しては隠せますが、LLM プロバイダには生データが送られるため、外部への漏えい対策にはなりません。PII の検出と除去には AI PII Sanitizer(Kong Gateway 3.10 以降・AI Gateway Enterprise)を使ってください。
プラグインはどのスコープに設定できますか?
request-transformer はグローバル・サービス・ルート・コンシューマーのいずれにも設定できます。ただしコンシューマー単位の設定が効くのは、認証プラグインがコンシューマーを特定した後のリクエストだけです。ai-request-transformer はスキーマで no_consumer が宣言されているため、そもそもコンシューマー単位では設定できません。
設定を変更したのに反映されません。どこを確認すればよいですか?
まず適用順を確認します。ヘッダーは remove、rename、replace、add、append の順に処理されるので、同じキーを複数の操作に書いていると後の操作で上書きされます。次に replace の挙動です。replace は対象が存在する場合のみ値を差し替えるため、存在しないキーを置換しようとしても何も起きません。新規に追加したいなら add を使います。テンプレートで値を組み立てている場合は、参照先が変換前のスナップショットである点も確認してください。