n4nAI

Client-side vs server-side API key handling for LLM apps

Compare client-side vs server-side api key handling for LLM apps across security, cost, latency, and ergonomics, with a verdict per use case.

n4n Team4 min read859 words

Audio narration

Coming soon — every post will get a voice note here.

Shipping an LLM provider key to a browser or mobile client feels like a shortcut until the key shows up in a public GitHub repo or a user intercepts the traffic. The tradeoff between client-side vs server-side api key handling determines who controls the credential, who pays for tokens, and how resilient your app is when a model provider degrades. This article breaks down both patterns across the dimensions that actually matter in production.

Defining the two patterns

Client-side key handling

In this model, the API key is embedded in code that runs on the user’s device. A React app might call OpenAI directly:

// danger: VITE_OPENAI_KEY is bundled into client JS
const res = await fetch("https://api.openai.com/v1/chat/completions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${import.meta.env.VITE_OPENAI_KEY}`,
  },
  body: JSON.stringify({ model: "gpt-4o", messages: [{ role: "user", content: "hi" }] }),
});

The key is recoverable from the network tab or the minified bundle. Providers explicitly forbid this in their terms for production.

Server-side key handling

The key lives in an environment variable on a backend you control. The frontend talks to your endpoint; your endpoint attaches the key.

# FastAPI proxy snippet
import os
from fastapi import FastAPI, Request
import httpx

app = FastAPI()
PROVIDER_KEY = os.environ["PROVIDER_KEY"]

@app.post("/chat")
async def chat(req: Request):
    payload = await req.json()
    async with httpx.AsyncClient() as client:
        r = await client.post(
            "https://api.openai.com/v1/chat/completions",
            headers={"Authorization": f"Bearer {PROVIDER_KEY}"},
            json=payload,
        )
    return r.json()

You can swap the provider URL for an OpenAI-compatible gateway without touching the client.

Dimensions of comparison

Capabilities

Client-side gives you direct access to provider features with zero middleware. You can stream responses straight to the UI. But you cannot enforce server-side rate limits, audit logs, or per-user quotas. Server-side lets you inject routing logic, cache responses, and apply fallback when a provider is throttled.

Price and cost model

With client-side handling, the bill goes straight to the provider from the key owner’s account. There is no intermediary margin, but abuse is unbounded—any leaked key runs up your tab. Server-side centralizes spend. You can meter per tenant and absorb infrastructure cost. A gateway such as n4n.ai provides per-token usage metering across 240+ models behind one OpenAI-compatible endpoint, which simplifies reconciliation if you already route through it.

Latency and throughput

Client-side saves one network hop: the device talks directly to the provider. On a good connection this trims a single network round-trip, typically tens of milliseconds. Server-side adds a proxy hop but enables connection pooling and regional colocation. More importantly, server-side can honor fallback directives—if the primary provider is rate-limited, the gateway can retry another without the client noticing.

Ergonomics

For a weekend prototype, client-side is unbeatable: set an env var, ship. Rotation means rebuilding the client. Server-side requires deploying a service, managing secrets, and handling CORS. However, once you have a backend, adding new models or providers is a config change, not a client release.

Ecosystem

Most provider SDKs are designed for Node or Python servers. Browser builds often need shims and expose fewer features (no async batch, limited retry). Server-side uses the full SDK surface. Gateways that are OpenAI-compatible let you keep the same client code while swapping the base URL.

Limits

Client-side is constrained by browser CORS policies and provider terms that prohibit key exposure. You also can’t set IP allowlists. Server-side is limited by your backend’s scalability—if your proxy is a single Lambda, you’ll hit concurrency caps.

Head-to-head table

Dimension Client-side Server-side
Capabilities Direct provider calls, no server logic Proxy, rate limits, logging, fallback
Cost model Direct provider billing, abuse risk Centralized metering, infra overhead
Latency One less hop, lower baseline Extra hop, but connection reuse
Ergonomics Zero backend, painful rotation More setup, flexible iteration
Ecosystem Partial SDK support, CORS issues Full SDK, gateway compatible
Limits Provider ToS, no IP lock Backend scaling ceiling

Security reality

The core issue with client-side vs server-side api key handling is trust boundary. A key in the client is public. Anyone with the bundle can extract it and run up spend or pull data accessible to that key. Server-side keeps the secret in a process boundary you control. Even if you trust users, a compromised CDN or browser extension can exfiltrate it.

Rotate keys by revoking at the provider; with client-side you must ship an update and hope users refresh. Server-side rotation is instantaneous.

When client-side is acceptable

Only for local-only tools or personal scripts where the key owner is the sole user. Never in a distributed binary or web app.

Server-side proxy pattern with a gateway

A practical server-side setup points the SDK at a gateway that fronts multiple models:

from openai import OpenAI
import os

client = OpenAI(
    base_url="https://api.n4n.ai/v1",  # OpenAI-compatible, 240+ models
    api_key=os.environ["N4N_API_KEY"],
)
# Automatic fallback and cache-control forwarding handled by gateway
resp = client.chat.completions.create(
    model="anthropic/claude-3.5-sonnet",
    messages=[{"role": "user", "content": "Summarize this."}],
)

The key stays server-side; the client calls your /chat endpoint. You get provider abstraction without leaking credentials.

Which to choose

Public web or mobile app

Use server-side. Exposing keys in a shipped artifact is a breach waiting to happen. Proxy through a backend that injects the key.

Internal admin tool behind SSO

Server-side still preferred, but you can relax if the network is isolated. Keep the key in a secret manager.

Local CLI or personal notebook

Client-side is fine. The key never leaves your machine. Use an env file with 0600 perms.

Rapid prototype for a demo

Client-side gets you moving, but set a low usage limit on the provider key and rotate immediately after.

Multi-tenant SaaS

Server-side mandatory. Centralize billing, per-tenant metering, and route via a gateway that supports fallback so one provider outage doesn’t page you.

The decision around client-side vs server-side api key handling is not about convenience; it’s about who holds the blast radius. If a leaked key can cost you real money or data, it belongs on a server you control.

Tagsapi-keysclient-sideserver-sidesecurity

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All api key authentication best practices posts →