API Key Header Leaked in Browser Network Tab, How to Route via Proxy Backend
An API key visible in the browser's Network tab is exposed to anyone with access to developer tools — which is effectively any user of your site — regardless of whether the key appears in JavaScript source, a request header, or a query parameter. The fix is architectural: the browser should never see the real key at all.
The Problem
Opening browser developer tools and inspecting the Network tab reveals a request containing a sensitive API key directly:
GET https://api.thirdparty.com/data
Headers:
X-API-Key: sk_live_abc123realkey456
Anyone using the site can open developer tools, inspect this request, and extract the key for their own unauthorized use, entirely independent of how well-obfuscated or minified the surrounding JavaScript code might be.
Why It Happens
If a frontend application makes a request directly to a third-party API using a real API key, that key has to be included somewhere in the request the browser sends — and anything the browser sends is visible to the user controlling that browser, full stop. No amount of code obfuscation, minification, or "hiding" the key in a less obvious place in the JavaScript bundle changes this fundamental fact, since the actual network request itself, inspectable in plain text via developer tools, is where the exposure genuinely happens.
The Fix
Route the request through your own backend, which holds the real API key server-side, where the browser never has access to it at all:
// Frontend: calls your own backend, never the third-party API directly
const response = await fetch('/api/proxy/thirdparty-data');
const data = await response.json();
// Backend: makes the actual third-party request, using the real key server-side
app.get('/api/proxy/thirdparty-data', async (req, res) => {
const response = await fetch('https://api.thirdparty.com/data', {
headers: { 'X-API-Key': process.env.THIRDPARTY_API_KEY },
});
const data = await response.json();
res.json(data);
});
With this pattern, the browser's network tab only ever shows a request to your own backend, with no visibility into what your backend does server-side to fulfill that request — the real API key never travels to or through the browser at any point.
If your backend proxy endpoint accepts any parameters from the frontend that get forwarded to the third-party API, validate and sanitize them server-side before forwarding, rather than passing frontend input through unchecked — this prevents the proxy endpoint itself from becoming a new kind of vulnerability, where a malicious user could manipulate parameters to abuse the third-party API in ways you didn't intend, using your legitimate, correctly-hidden API key as the vehicle:
app.get('/api/proxy/thirdparty-data', async (req, res) => {
const allowedParams = ['category', 'limit'];
const safeParams = {};
for (const key of allowedParams) {
if (req.query[key]) safeParams[key] = req.query[key];
}
const response = await fetch(`https://api.thirdparty.com/data?${new URLSearchParams(safeParams)}`, {
headers: { 'X-API-Key': process.env.THIRDPARTY_API_KEY },
});
res.json(await response.json());
});
Add appropriate rate limiting to your own proxy endpoint as well, since it's now effectively a new public entry point to the third-party service, and without your own rate limiting, abuse of your proxy could still exhaust the underlying third-party API quota (using your legitimate key) even though the key itself is no longer directly exposed.
For third-party services that offer separate "public" and "private" key types specifically designed to be safely exposed client-side (with restricted permissions or scoped usage), confirm you're using the intentionally public variant in frontend code, and reserve the private, more powerful key exclusively for server-side use through the proxy pattern above.
Still Not Working?
If routing through a backend proxy isn't immediately practical for your architecture (a fully static site with no backend, for example), and the third-party service genuinely requires a key to be used client-side, check whether that service offers domain restriction or referrer-based restrictions on API keys — many services let you restrict a specific key to only function when requests originate from your specific registered domain, which doesn't prevent the key from being visible, but significantly limits what a malicious actor can actually do with a copied key, since their own unauthorized usage from a different domain would be rejected by the third-party service itself, independent of your own application's code.