Forward Proxy for IDE AI Traffic
Enterprises often need visibility into the AI traffic their developer tools send — prompts and code context leaving employee machines for api.githubcopilot.com and similar official backends. This guide deploys AISIX as the plaintext receiving side of a forward-proxy chain: a TLS-terminating egress device (operated by you — AISIX performs no TLS interception) delivers decrypted HTTP to the gateway, which audits, applies guardrails, rate-limits, and relays each request to its official backend unchanged, with the employee's own credential.
The IDE needs zero modification: it keeps its official service, its own short-lived tokens, and its normal proxy settings.
Traffic Topology
The gateway's receiving contract is ordinary HTTP: origin-form requests carrying the original Host (transparent redirect and proxy chaining both satisfy this; absolute-form request targets from a chained proxy are also accepted). A route whose hosts allowlist matches the inbound host is dispatched before the gateway's own path routing, so a Copilot request to /v1/chat/completions can never fall into the gateway's typed chat endpoint.
Route the Copilot Hosts
Copilot's host set is published in GitHub's allowlist reference. Two classes matter here: the AI endpoints worth auditing, and auxiliary traffic to relay untouched (or route around the gateway entirely).
Create one route per class. The chat hosts speak an OpenAI-compatible chat shape (note: no /v1 prefix on api.githubcopilot.com); the completion hosts speak the legacy fill-in-the-middle shape:
passthrough_routes:
# Copilot Chat — audit + DLP + usage extraction
- name: copilot-chat
hosts:
- api.githubcopilot.com
- "*.individual.githubcopilot.com"
- "*.business.githubcopilot.com"
- "*.enterprise.githubcopilot.com"
preserve_host: true # target = https://<matched host>
auth_mode: header_key # gateway key injected by the device
auth_header_name: x-aisix-api-key
credential_mode: forward_client # the employee's Copilot token goes upstream
protocol: openai_responses # Copilot posts inference to /responses
identity_header: x-aisix-user # device-injected employee identity
# Copilot code completions (FIM)
- name: copilot-completions
hosts:
- "proxy.individual.githubcopilot.com"
- "proxy.business.githubcopilot.com"
- "proxy.enterprise.githubcopilot.com"
- copilot-proxy.githubusercontent.com
preserve_host: true
auth_mode: header_key
auth_header_name: x-aisix-api-key
credential_mode: forward_client
protocol: openai_completions
identity_header: x-aisix-user
# Copilot CLI's GitHub MCP server — same hosts as chat, different
# protocol under /mcp, so it needs its own route (see Copilot CLI below)
- name: copilot-cli-mcp
hosts:
- api.githubcopilot.com
- "*.individual.githubcopilot.com"
- "*.business.githubcopilot.com"
- "*.enterprise.githubcopilot.com"
path_prefix: /mcp
preserve_host: true
auth_mode: header_key
auth_header_name: x-aisix-api-key
credential_mode: forward_client
identity_header: x-aisix-user
# Token exchange and other GitHub API traffic — pure relay, no parsing
- name: copilot-auxiliary
hosts: [api.github.com]
preserve_host: true
auth_mode: header_key
auth_header_name: x-aisix-api-key
credential_mode: forward_client
Grant the routes on the gateway API key the device injects:
api_keys:
- display_name: egress-device
allowed_models: []
allowed_routes: [copilot-chat, copilot-cli-mcp, copilot-completions, copilot-auxiliary]
Login traffic (github.com/login/*) and telemetry hosts can bypass the gateway at the device — they carry no prompt content. Anything not diverted to the gateway simply never reaches it.
GitHub Copilot CLI (Agent)
The Copilot CLI is a terminal agent, not an editor plugin: it reasons, calls tools, edits files, runs shell commands, and talks to an MCP server. Its traffic differs from the IDE plugin traffic in one way that matters for routing — it reaches its GitHub MCP server at /mcp/readonly on the same host it uses for chat inference. One host, two protocols, split by path.
That is why the route table above carries a separate copilot-cli-mcp route. Two properties make it work:
- A route that matches on
hostsmay claim a path prefix the gateway otherwise reserves for its own endpoints (/mcp,/v1,/a2a, …). Host-matched requests are dispatched ahead of the gateway's typed routes, so nothing shadows them. A route withouthostsstill cannot claim those prefixes. - A
preserve_hostroute treatspath_prefixas a match condition, not a mount point: the upstream owns its path space, so the complete path is forwarded. (Atarget_urlroute mounts at its prefix and strips it — the ordinary prefix-proxy behavior.)
Leave the MCP route on the default raw protocol: MCP is JSON-RPC over HTTP with streamed responses, not a chat body, so it is relayed verbatim while still being authenticated, rate-limited, and attributed.
Set the chat route's protocol to openai_responses, not openai_chat. The CLI posts every inference turn to /responses, whose envelope the chat hint cannot read: the requests would still be authenticated, audited, and attributed, but they would record zero tokens, leaving token rate limits, spend, and usage reporting blind to all agent traffic.
The endpoints one CLI session touches:
| Traffic | Endpoint | Route |
|---|---|---|
| Agent inference | POST /chat/completions on the chat hosts | copilot-chat |
| MCP session (initialize, tool calls, shutdown) | POST/GET/DELETE /mcp/readonly | copilot-cli-mcp |
| Entitlement and policy | GET /copilot_internal/user, /copilot_internal/managed_settings | copilot-auxiliary |
| Model catalog | GET /models on the chat hosts | copilot-chat |
| Version check | GET /repos/github/copilot-cli/releases/latest | copilot-auxiliary |
Nothing about the CLI needs configuring: it honors the machine's proxy settings, so the egress device sees its traffic like any other client's.
Once routed, the agent behaves normally end to end — it reasons, reads and writes files, runs shell commands, and loads its MCP tools (the /context view in the CLI shows the MCP tool definitions occupying context, which only happens when the MCP session established through the gateway). Interactive commands work as usual; the ones that reach the network (/model, /context, /usage, /mcp, /review, /security-review, …) travel the same routes as the agent's own turns and are audited alongside them.
Point the CLI Straight at the Gateway Instead
The CLI also supports bring-your-own-key mode, which removes the TLS-terminating device from the picture entirely:
export COPILOT_PROVIDER_BASE_URL=https://gateway.example.com/copilot-cli
export COPILOT_PROVIDER_TYPE=openai
export COPILOT_PROVIDER_BEARER_TOKEN=<upstream credential>
export COPILOT_MODEL=<model>
export COPILOT_PROVIDER_HEADERS="x-aisix-api-key: <gateway key>
x-aisix-user: employee@example.com"
The matching route is an ordinary prefix route — no host matching, no interception:
passthrough_routes:
- name: cli-byok
path_prefix: /copilot-cli
target_url: https://api.example-provider.com/v1
auth_mode: header_key
auth_header_name: x-aisix-api-key
credential_mode: forward_client
protocol: openai_chat
identity_header: x-aisix-user
This mode sends inference to whatever provider the route targets rather than to Copilot's backend, so the GitHub-side features of the CLI (MCP server, entitlement checks) are not involved. Choose it when the goal is to run the agent against a model you control; choose the forward proxy when the goal is to audit employees' use of Copilot itself.
Gateway Authentication Options
The employee's request carries their own Copilot token in Authorization, so the gateway credential needs another channel:
header_key(shown above) — the egress device injectsx-aisix-api-key: <gateway key>into each forwarded request. The gateway consumes and strips that header;Authorizationpasses to the upstream verbatim. Per-key rate limits, budgets, and attribution work normally.anonymous— if the device cannot inject headers, bind the route to a dedicated API key and restrictsource_cidrsto the device's addresses. Traffic runs as that principal with its limits and attribution; network reachability is the gate.
Per-Employee Attribution
AISIX does not parse the Copilot token (it carries no enterprise identity). If the device can inject the employee identity — for example from its own authentication — name that header as the route's identity_header. Its value is stripped before forwarding and recorded on every usage event as client_identity, searchable on the environment's Logs page. Without header injection, the recorded client source IP is the fallback attribution.
Audit, DLP, and Limits
- Content audit — configure an observability exporter with
content_mode: full; chat prompts/responses and completion contexts then record to the exporter destination (structured, since the routes declare protocol hints). See Observability Exporters. - DLP — attach guardrails to the routes (the
passthrough_routeattachment scope). A hit blocks the request with422before it leaves the network, or ends a streaming response with acontent_filtererror frame before held frames are released. See Guardrail Behavior. - Rate limits — the injected gateway key's (or anonymous principal's) request limits apply. Token usage is recorded from the protocol-aware parsing, so token dimensions accumulate too.
Validate Without the Production Device
Any TLS-terminating proxy that forwards decrypted requests with their original Host can stand in for the production device. With mitmproxy, a small script that rewrites each decrypted flow's destination to the gateway (keeping the Host header) and injects the two gateway headers reproduces the topology on one machine:
- Point the IDE at the proxy (VS Code:
http.proxy, plus trusting the mitmproxy CA). - Script: for flows whose host matches the Copilot set, set
flow.request.host/port/schemeto the gateway listener and addx-aisix-api-keyandx-aisix-user. - Use Copilot normally. Chat and inline completion behave unchanged (streaming relays incrementally); the environment's Logs page shows the traffic attributed to the routes and identities, and the exporter receives the content records.
Streamed completions flow through frame-by-frame unless a hold-back guardrail is attached, in which case frames release in scanned batches.
Scope and Limits
- WebSocket traffic is not relayed by passthrough routes — divert it around the gateway at the device.
preserve_hosttargets are alwayshttps://<host>on port 443.- AISIX performs no TLS interception and ships no CA tooling; the decrypting device is operated by you.