Security
How to use FeatureToggle safely in development and production.
API keys
Read-only, not secret
An API key grants read access to all enabled features for one environment. It cannot create, modify, or delete features.
The public API accepts cross-origin requests with a valid Bearer token. Treat every key as capable of being reused if leaked.
Key prefixes and placement
| Prefix | Environment | Where to use |
|---|---|---|
ft_test_ | development | Browser on localhost only |
ft_live_ | staging, production | Server-side code and trusted backends |
API keys in browser bundles are public. Any key in VITE_*, NEXT_PUBLIC_*, or inlined env vars can be extracted from client JavaScript. Never embed ft_live_ keys in client bundles.
Test key scope
ft_test_ keys return 403 when used from non-loopback origins:
- ✓
http://localhost:3000 - ✓
http://127.0.0.1:3000 - ✓
http://[::1]:3000 - ✗
https://myapp.vercel.app - ✗
http://myapp.local:3000 - ✗ Private LAN IPs (RFC1918):
10.0.0.0/8(10.x.x.x)172.16.0.0/12(172.16.x.x-172.31.x.x)192.168.0.0/16(192.168.x.x)
Free plan: test keys work only on true loopback. RFC1918 origins are always blocked, including phone-on-WiFi dev servers (http://192.168.x.x:3000).
Paid plan: same loopback rule for ft_test_. To test from a LAN IP or deployed URL, Add staging or Add production, create an ft_live_ key, and use the server SDK or a trusted backend.
Storage on our side
Only a SHA-256 hash of the key is stored. The full key is shown once at creation. Revoke compromised keys under Environment → API keys immediately.
On 401, the SDK clears its cache and stops the SSE stream.
Live keys and deploy access
ft_live_ keys may return 403 when the account does not have deploy access to staging or production, or when a subscription is inactive. The key may still appear in the dashboard. Use Add staging / Add production and Upgrade plan when needed, or create keys only for environments the account can access.
Features are not authorization
Client-side checks (isEnabled(), useFeature(), getValue()) control UX only. Users can bypass them via devtools, modified clients, or direct API calls with the API key.
Use server-side evaluation for security boundaries:
import { FeatureToggleServer } from "featuretoggle-sdk-typescript/server";
const ft = await getServerFt();
await ft.refresh();
if (!ft.isEnabled("admin-panel")) {
return new Response("Not found", { status: 404 });
}Gate redirects, API responses, billing features, and sensitive routing on the server. Browser checks alone are not enough.
Patterns: TypeScript cookbook: API route gate
Sanitize feature values
JSON from getValue() is untrusted data. Do not inject into HTML without escaping. Do not eval() or execute feature values as code.
SSR and test keys
Server fetch without an Origin header returns 403 for test keys. Options:
- Use
featuretoggle-sdk-typescript/serverwith a live key in deployed environments. - Pass a custom
fetchwithOrigin: http://localhost:<port>for local SSR. - Use client-only
init()on localhost for browser hydration.
Do not seed features in HTML or loader data that must stay server-only. The initialFeatures / SSR seed patterns expose feature state to the client.
Server singleton concurrency
One FeatureToggleServer per process is typical. Under concurrent requests:
- Await refresh() before reads, or
- Create a per-request instance for strict isolation.
Otherwise callers may see transient stale cache state.
Custom fetch
The optional fetch constructor option is for unit tests. Do not log Authorization headers in production wrappers.
CORS
The feature API allows browser requests from any origin with a valid key. This is intentional for SPAs, but it also means a leaked browser key can be called from any site. Scope keys per environment and revoke on compromise.
Dashboard security
- Management requires a signed-in user session scoped to the organization.
- All data is scoped to the organization.
- There is no public API for admin operations.
Checklist before production
- Live key (ft_live_) only in server env vars, never in the client bundle
- Sensitive features evaluated in API routes / middleware
- Compromised keys Revoked under Environment → API keys
- Deprecated features migrated; warnings reviewed
- Add staging or Add production, then create an ft_live_ key under API keys for deployed apps