> ## Documentation Index
> Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Securing Your Key

> What the Helius API key your embedded-wallet app exposes can and can't do, and how to lock it down with access control, Secure RPC URLs, and a proxy.

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](https://www.turnkey.com)'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.

> [!NOTE]
>
> **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](https://dashboard.helius.dev), 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:

```
yourdapp.com
www.yourdapp.com
staging.yourdapp.com
```

**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.

> [!NOTE]
>
> **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](#4-add-a-non-spoofable-ipcidr-boundary)).

### 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](https://www.helius.dev/docs/sending-transactions/sender.md) 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.

  ```ts app/api/helius/[...path]/route.ts theme={"system"}
  import { createHeliusRouteHandler } from "helius-wallet-kit/next";

  export const { GET, POST } = createHeliusRouteHandler();
  ```

* **Cloudflare Worker proxy** — the cleanest fully-serverless option: the key lives in a Worker secret and never reaches the browser.

  > [card] [Helius RPC Proxy](https://github.com/helius-labs/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**:

| Key | Used by | Restriction |
| - | - | - |
| Public / browser key | provider `config` (bootstrap) | Allowed **Domains** |
| Server key | route handler / Worker (RPC, send) | Allowed **IPs/CIDRs** |

> [!NOTE]
>
> 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](https://www.helius.dev/docs/rpc/protect-your-keys.md) for the full access-control rule reference.

## How the layers compare

| Layer | Key in browser? | Forgeable? | Effort |
| - | - | - | - |
| Domain-restricted key | Yes | Origin header is forgeable | Lowest |
| Key-less Secure RPC URL | No key in RPC | N/A — no key, rate-limited | None (default) |
| Server-side key (route handler/Worker) | No | — | Low |
| Server-side key + IP/CIDR | No | Source IP is **not** forgeable | Medium |

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

## The wallet bootstrap

> [!NOTE]
>
> 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

> [card] [Protect your keys](https://www.helius.dev/docs/rpc/protect-your-keys.md)
>
> Full access-control reference: domains, IPs, CIDRs, and proxies.

> [card] [Configuration](https://www.helius.dev/docs/waas/configuration.md)
>
> Provider config and dashboard sign-in methods.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.