LLM security

The proxy layer that keeps your model keys off the client.

If a browser can read your provider key, so can anyone with devtools. A secure LLM proxy puts that call behind your own server, and gives the boundary limits that hold.

The brief, honestly

Your key is one view-source away.

The pattern arrives the same way every time. Somebody ships something with a model in it quickly, the fetch call lives in the front end because that was the shortest path, and the provider key ends up in a JavaScript bundle. Nothing bad has happened yet. That is not the same as safe.

The fix is a boundary you own. Requests go to your server, your server holds the key and talks to the provider, and the browser never sees a credential. Then the boundary earns its keep: origin validation, IP rate limiting, nonce verification, prompt-length control and a token budget per user.

We have built exactly this for a product the public uses. Froala's AI Assist runs through a WordPress plugin we wrote, and every control named above is in it, in production. The section further down lists them, along with the parts most teams skip until an invoice makes them care.

Problem 01

Our API key is in the front-end bundle.

The key moves to your server today and the client gets a route instead. Then we rotate the exposed key — anything that has been public stays public.

Problem 02

Someone could run up our model bill overnight.

IP rate limiting, a token budget per user and per day, and a hard ceiling that returns an error rather than a charge. Limits before launch, not after the invoice.

Problem 03

Our plugin is already published in the wild.

Retrofit is the normal case, not the awkward one. The client-side call becomes a call to your route, shipped as a point release your users install like any other update.

Problem 04

The provider had an outage and our feature just died.

Provider fallback wired into the boundary, with the second provider tested on a drill rather than discovered during an incident.

What's included

Scope, spelled out.

API key protection

The provider credential lives in your server environment only. The browser gets a route on your domain and never a key it could read.

Origin and referer checks

Origin validation and referer checks on every request, so your route answers your own pages and quietly refuses everybody else's.

IP rate limiting

Per-IP request ceilings held in short-lived server storage, so one script cannot turn your model spend into somebody's weekend project.

Nonce verification

Every request carries a short-lived token issued by your own page, so replayed and forged calls are rejected before they reach the provider.

Prompt-length control

A cap on what a caller may send, enforced server-side — an unbounded input field is a billing vulnerability with a cursor in it.

Token budget and logging

Per-user and per-day budgets with usage written to your own log, so the ceiling is enforced by code rather than watched in a dashboard.

Provider fallback

A second provider configured and drilled, plus a graceful degrade path, so an outage upstream is a slower response and not a broken page.

How we work

What actually happens, week by week.

01

Exposure review

We look for the key: bundles, network tab, repo history, config files. Usually the fastest part of the job.

02

Boundary design

One route on your domain, the key in your server environment, and a request shape the client can't widen.

03

Abuse controls

Origin validation, IP rate limiting, nonce verification and prompt-length caps, enforced server-side.

04

Budget & logs

A token budget per user and per day, with usage logged where your own team can read it.

05

Fallback drill

We turn the primary provider off on purpose and watch what your users actually see.

Tech stack

The tools we actually use here.

The boundary should be boring: your server, your logs, your bill. It belongs wherever your app already lives, so this list is runtime and storage with the providers on the end.

PHPLaravelSlimNode.jsWordPressMySQLApacheAWSGoogle CloudDigitalOceanAnthropic ClaudeOpenAIDeepSeekOpenRouter
What you get

Deliverables, outcomes and who this is for.

Deliverables
  • Key exposure report
  • Server-side proxy route
  • Origin, referer and nonce checks
  • IP rate limits and prompt caps
  • Token budget with usage logging
  • Provider fallback, drilled
Outcomes
  • No provider key reachable from a browser
  • A spend ceiling enforced in code
  • Abuse stopped at your own domain
  • An outage degrades instead of breaking
Ideal client

Anyone whose product calls a model, especially plugin authors and teams whose front end talks to a provider directly.

Proof

We've shipped this exact thing, in public.

Froala AI Assist, the whole control set

Froala's AI Assist reaches DeepSeek through a WordPress plugin we built, with the credential server-side. Requests are checked for origin and referer, rate-limited per IP using WordPress transients, verified with a nonce from the calling page, and rejected if the prompt is over its length cap. Shipped, publicly.

The controls nobody asks for until later

Origin validation and rate limiting get budgeted. The token budget and the usage log rarely do, and they are the two that decide whether a bad week is an annoyance or a finance meeting. We put a hard per-user ceiling in the code path, not a threshold alert in a dashboard somebody eventually mutes.

Reading the logs when it has gone wrong

Our Filestack engagement started in the server logs and ended in plugin security fixes. Order matters when a route is already being hammered: the traffic pattern tells you which control is missing, and guessing instead usually adds the expensive one first and leaves the gap open.

What a retrofit involves

Retrofitting a plugin already in the wild is the common case and it is smaller than it sounds. The front-end call is repointed at your own route, the credential moves into server configuration, the abuse controls sit behind it, and the exposed key is rotated because strangers have already read it. One release.

Frequently asked

Questions we get on the first call.

The exposure closes in one short, scoped piece of work: one route, the key moved server-side, the old key rotated. The abuse controls follow in the same engagement. Price depends on where your code lives and how many callers you have to keep working, which is a scoping question we answer before you commit. Rotate first, argue later.

It adds one hop, and the model call dominates the round trip by a wide margin, so users rarely notice. We won't quote you milliseconds we haven't measured on your infrastructure. What we will do is put the proxy in the same region as the caller, stream the response, and measure it before and after.

Yes, and we have. The plugin behind Froala's AI Assist is exactly that shape: a published WordPress plugin whose model calls all run through a hardened server-side route. The awkward part is never the code. It is deciding which old versions you still support while the exposed key gets rotated.

You do. We are not your insurer, and any vendor who promises to cover a provider invoice is selling something. What we will do is put the cheap controls in first, on day one: the key off the client, a hard request ceiling, a prompt-length cap. The expensive engineering can wait. The ceiling can't.

By default we log metadata and not content: caller, timestamp, token count, outcome. That is enough to enforce a token budget and to investigate abuse. If you want prompt bodies stored, it is your database, your retention window and your access list, written down before we build it. We don't keep a copy.

Get the key off the client, this week.

Send us the URL and we'll tell you on the call whether your key is reachable from a browser. Bring the repo if you'd rather we just look.