Skip to main content
The wallet-kit SDK runs in the browser, so your Helius apiKey is present client-side: the provider sends it to the wallet bootstrap (/waas/config) when your app loads. This is expected for a client-side SDK. This page explains exactly what that key can and can’t do, and how to lock it down.

What the key can — and can’t — do

Start here, because it’s the part that’s easy to get wrong: the Helius API key is an RPC/credits credential, not a wallet key. A leaked key cannot:
  • sign transactions or messages,
  • move or access funds, or
  • touch any user’s embedded wallet.
Wallet signing is authorized by the end user’s own passkey or session, and the private keys live in Turnkey’s secure enclaves — never in your app, your server, or anything the API key can reach. Exposing the key does not expose wallets. What a leaked key can do is consume your Helius credits (RPC quota). That’s the entire blast radius — a billing concern, not a custody one — and the layers below bound and close it.
Exposure is not a billing bypass either. WaaS signatures are metered at the moment the embedded wallet signs, not at the API-key layer — a leaked key can’t mint free signatures or bill signatures from someone else’s site. The metering doesn’t depend on key secrecy.

Lock it down

These layers go from lowest-effort to the hardest boundary. The first is the required baseline; stack the rest as your risk tolerance requires.

1. Domain-restrict your key (required)

Lock the key to the origins your app runs on, so a key scraped from your bundle is useless from anywhere else.
1

Open RPC Access Control

In the dashboard, go to your key under the RPCs section and open Access Control.
2

Add your domains to Allowed Domains

Add every origin your app serves from — production, staging, and preview:
3

Use a separate key per environment

Keep a distinct key for local, staging, and production so you can rotate one without taking down the others.
Domain allowlists stop browser-based misuse, not a determined script. The check reads the request’s Origin/Referer header — a browser sets it honestly, but a non-browser client (e.g. curl -H "Origin: yourdapp.com") can forge it. This is true of every client-side API key, not just Helius. Domain restriction reliably stops the common case — your key showing up on someone else’s site — but for a boundary that can’t be forged, use a server-side key locked to your IPs/CIDRs (step 4).

2. Key-less Secure RPC URLs (automatic)

RPC traffic doesn’t carry your key at all. The SDK resolves your project’s key-less Secure RPC URL at bootstrap and uses it for connection calls automatically — nothing to configure. Because these URLs contain no key, there’s nothing in an RPC request to extract, and they’re rate-limited to 5 RPS per IP — so their protection doesn’t depend on Origin checks. (Available on paid plans; when a project has no Secure RPC URL, RPC falls back to the same-origin route handler in step 3.)

3. Move the key server-side — serverless

To keep the key out of the browser for RPC, send, and transaction-history calls, route them through your own endpoint that injects the key from a server-side secret. This is also how you opt into Sender-optimized landing and transaction history. Both options are 100% serverless — no server to run:
  • Next.js route handler — deploys as a serverless function (Vercel, Netlify, Cloudflare). Reads HELIUS_API_KEY from the server environment.
    app/api/helius/[...path]/route.ts
  • Cloudflare Worker proxy — the cleanest fully-serverless option: the key lives in a Worker secret and never reaches the browser.

    Helius RPC Proxy

    Open-source RPC proxy you deploy to Cloudflare with one click.

4. Add a non-spoofable IP/CIDR boundary

For a boundary an attacker cannot forge, lock a server-side key to your backend’s IP addresses or CIDR ranges. Unlike an Origin header, a request’s source IP can’t be spoofed over a normal connection — so a curl from anywhere but your servers is rejected outright. This applies to a server-side key only — you can’t IP-restrict the browser key, because your users connect from unpredictable IPs. The clean pattern is two keys:
Serverless functions have dynamic egress IPs, so pinning a CIDR needs a stable egress — a Cloudflare Worker with a dedicated egress IP, Vercel Secure Compute, or a fixed-IP NAT in front. Without one you still get the main win (the key is server-side, never in the browser); you just don’t add the IP boundary on top.
See Protect your keys for the full access-control rule reference.

How the layers compare

Whichever layers you choose, none of this is your users’ wallet keys — those are never in play.

The wallet bootstrap

In the route-handler (production) setup, the wallet bootstrap (/waas/config) also goes through your route handler with the server-side key — so no Helius key ships to the browser for it either. In the prototyping setup, the browser sends the key for the bootstrap; domain-restrict it (step 1). Either way it’s the RPC-scoped key — it can’t touch wallets or funds.

Checklist

Before you ship:
  • Client key is domain-restricted to your exact origins
  • Separate keys for local / staging / production
  • Key is read from an env var, never hardcoded
  • (Optional) RPC, send, and history routed through the serverless route handler or Cloudflare Worker
  • (Optional) Server-side key locked to your IPs/CIDRs for a non-spoofable boundary

Next steps

Protect your keys

Full access-control reference: domains, IPs, CIDRs, and proxies.

Configuration

Provider config and dashboard sign-in methods.