GitHub Copilot trusted a client header for premium-request billing

j0selit01 pts0 comments

I Changed One HTTP Header in GitHub Copilot. It Stopped Charging Me.

Lighthouse AI

SubscribeSign in

I Changed One HTTP Header in GitHub Copilot. It Stopped Charging Me.<br>How Copilot’s internal user-vs-agent distinction turned into a classic client-trust bug

Rafael Pierre<br>Aug 19, 2026

Share

Hello, Rafael here - every week I cover interesting challenges and developments that I’ve come across recently through the lens of an engineer building AI systems.<br>Subscribe and get my weekly takes 👇

Subscribe

Previously, I showed how I intercepted GitHub Copilot’s network traffic in order to understand how it works. Some interesting takeaways emerged from that exercise, but the most surprising finding was still yet to come. This is part 2 of that saga.

I put GitHub Copilot Behind a MITM Proxy. Here's What I found.<br>Rafael Pierre<br>Aug 4

Read full story

Some orphaned LLM requests

Back when I was analysing GitHub Copilot traffic, I noticed something intriguing. Some of the model requests were not mine. As in, they were not triggered by any of my messages in GitHub Copilot chat.<br>In addition to the LLM requests used to fulfil your prompts, Copilot also fires LLM requests on its own, for at least two reasons. The first is to come up with titles for your conversation. The second is to summarise the conversation up until a specific point.<br>I started to analyse those requests and responses, starting with headers. Immediately, one of them jumped out: X-initiator. For all LLM requests initiated by Copilot, this header is populated with agent.<br>I initially hypothesised that X-initiator was present in all LLM requests, and later confirmed that it was. For requests fired by Copilot, X-initiator is set to agent. For requests fired by me, X-initiator is set to user. It is simply a way to flag to Copilot Models API if a request was originated by Copilot itself or by the user.<br>Am I being scammed?

This whole network traffic analysis rabbit hole started around billing; I was trying to investigate and understand how and why my quota was being exhausted so quickly.<br>So after learning about this X-initiator header, it was only natural that my paranoid self’s follow up question would be: am I being charged by LLM requests that I didn’t initiate?<br>My security-conscious self had a different question: could an attacker spoof this header and get free LLM calls?<br>There was a simple way to answer both questions.<br>Are agent initiated requests billed?

Responses from Copilot’s Models API include updated quota-consumption data. This data is included in three distinct response headers:<br>x-quota-snapshot_premium-interactions: remaining premium-interaction quota; relevant to billing.

x-usage-ratelimit-session: remaining session-level allowance; apparently related to rate limiting or anti-abuse.

x-usage-ratelimit-weekly: remaining weekly allowance; apparently related to rate limiting or anti-abuse.

Looking at those headers on agent-initiated requests gave me the answer. My quota did not change after multiple agent-initiated LLM requests.<br>So no, I was not being charged by requests that I didn’t initiate, fortunately.<br>But this reinforced my curiosity around the second question.<br>Can X-initiator spoofing unlock free LLM requests?

There were different ways to test Copilot’s behavior and answer this question. I already had my network-inspection stack set up with mitmproxy, so I decided to stick with it.<br>Besides passive traffic inspection, mitmproxy also allows you to inject any header or payload data you want into HTTP requests that are proxied through it. The easiest way to do this is through mitmproxy’s Python API.<br>The addon below rewrites the X-initiator header from user to agent for /v1/messages requests sent to api.individual.githubcopilot.com through mitmproxy.<br>from mitmproxy import http

class InitiatorSpoof:<br>def request(self, flow: http.HTTPFlow) -> None:<br>if flow.request.pretty_host != "api.individual.githubcopilot.com":<br>return<br>if flow.request.path != "/v1/messages": # Claude models endpoint<br>return<br>if flow.request.headers.get("x-initiator") == "user":<br>flow.request.headers["x-initiator"] = "agent"

addons = [InitiatorSpoof()]

The second step was to start mitmweb and pass the header rewriter addon as an argument with the command below.<br>mitmweb -s spoof.py

A more instrumented version that parses all five x-quota-snapshot-* and x-usage-ratelimit-* response headers and logs per-request consumption deltas can be found here.<br>Lighthouse AI is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.

Subscribe

Spoofed requests: billing analysis

I performed 10 test requests total over a ~5-minute window against my own (paid) Copilot subscription. Here are the results.

The conclusion: by faking the data around who initiated the LLM request, GitHub Copilot didn’t charge it. This meant bad actors could basically get free tokens.<br>Caveat: although there appeared to be no restriction on which...

requests copilot header request initiator github

Related Articles