Environment Keys in Flagsmith: Client-Side and Server-Side
A complete guide to the two keys that connect every application to Flagsmith: what each one is, what it can and cannot do, where it may live, how to store, rotate, and protect it, and how to respond when something goes wrong.
A developer is putting the finishing touches on a new web page. The page needs a feature flag, so she opens the Flagsmith dashboard, finds a long string labelled as a key for the production environment, and pastes it into her front-end configuration. The page works. The flag toggles. She commits the change, and the deployment goes out before lunch. Nothing looks wrong. But which string did she copy? One of the two possible answers is perfectly harmless. The other quietly hands every visitor to the website a complete map of the company’s targeting rules.
Flagsmith gives every environment two different kinds of key, and they look deceptively similar: both are long, random-looking strings. They are used in the same place, a setting when you start the SDK. Yet they sit at opposite ends of the security spectrum. One is designed to be printed on a billboard. The other must be guarded like a bank vault combination. Confusing them is one of the most common and most avoidable mistakes in feature flag work.
This guide explains both keys from first principles. You will learn what each one is for, how Flagsmith treats a request that carries each one, which SDKs use which, how to store them, how to rotate them without downtime, how to detect and respond to a leak, and how to build habits that keep you out of trouble. No code is needed, and every technical word is explained the first time it appears.
AWhy Environment Keys Exist
Before comparing the two keys, we need to understand the two jobs a key performs: saying who is asking, and deciding what they may do.
Every request needs an address
Flagsmith serves flags for many customers, many projects, and many environments at the same time. When an SDK sends a request, Flagsmith must instantly know which environment’s flags are wanted. A production environment and a development environment may share a project, and even share feature names, but they hold different values. The key attached to each request is how Flagsmith finds the right one. In this sense, a key works like an address: it points at one specific environment and no other.
Every request also needs permission
An address alone is not enough. If knowing an environment’s address were all it took to read everything inside it, then anyone who learned the address could read everything. So Flagsmith attaches a second meaning to the key: a level of trust. Different keys are allowed to do different things. A key that is meant to be public may only perform a narrow set of low-risk actions. A key that is meant to be secret may perform broader actions, such as downloading the complete rulebook for the environment. The same idea exists in many systems: some identifiers are safe to show, and others are secrets.
Think of an apartment building. The street number painted on the front is an address: everybody knows it, and knowing it lets you ring the bell and ask a question at the door. The master key kept in a locked box in the manager’s office is a credential: it opens every flat. Flagsmith’s client-side ID is the street number. Flagsmith’s server-side key is the master key. Mixing them up means either hiding your street number for no reason, or painting your master key on the front door.
Why two keys, not one?
Modern applications run in two very different places. Some code runs on servers you control, where nobody outside your team can look inside. Other code runs on devices you do not control, such as web browsers and phones, where any user can inspect everything. A single key could not serve both safely. If it were secret, front-end code could never use it. If it were public, back-end code would be given too little power or the public would be given too much. Two keys solve this cleanly: a public one with limited abilities, and a secret one with greater abilities.
Names in the dashboard and the documentation have changed over the years. You may see older material that calls the public identifier an “environment API key” or similar. Always check which key is public and which is secret by reading the label next to it in the dashboard and in the current documentation, not by trusting the name you remember.
Three words that are often confused
Security conversations use three similar-sounding words, and keeping them apart makes keys easier to understand. Identification means saying who or what you are, such as giving a name. Authentication means proving it, such as showing a passport that others can check. Authorisation means the system deciding what you may do once it knows who you are. Environment keys touch all three, but not equally.
The client-side ID is mostly about identification: it says “I am asking about this environment”. It proves very little, since anyone can copy it, so it is a weak form of authentication, and that is why its powers are kept small. The server-side key adds real authentication: only someone who holds the secret can present it, so Flagsmith can trust that the caller is one of your own services. That trust is then turned into authorisation: a short list of things such a caller may do, including downloading the rulebook. The design principle is that the more a credential can do, the harder it must be to obtain, and the more carefully it must be kept.
A public identifier is a label, not a lock. Never design a system in which knowing a label is enough to do something dangerous. Flagsmith follows this rule: the label (client-side ID) can only do small, safe things, and anything larger needs the secret.
BThe Two Keys at a Glance
Here is the whole topic in one table. The rest of this guide expands every row.
| Question | Client-side environment ID | Server-side environment key |
|---|---|---|
| Is it secret? | No. It is designed to be visible | Yes. Treat it like a password |
| Where may it live? | Browsers, mobile apps, any client code | Backend services, workers, jobs, protected pipelines |
| Which SDKs normally use it? | Client-side SDKs: JavaScript, React, mobile, and similar | Server-side SDKs: Python, Node.js, Java, .NET, Go, Ruby, PHP, and similar |
| What can it read? | Flags and values for one identity at a time | The same, plus the complete environment document for local evaluation |
| What can it change? | Possibly an identity’s traits, depending on settings | Not flag definitions; it cannot edit features or segments |
| How many per environment? | One identifier | One or more, each named and revocable |
| If it leaks? | Little harm in itself, though some data is exposed by design | Serious: the whole targeting rulebook can be downloaded |
| How do you replace it? | Rarely needed; it identifies, not authenticates | Create a new key, deploy it, delete the old one |
The easy memory trick
Ask a single question: “could a stranger see this string?” If the code is running in a place a stranger can open, such as a web page, a phone app, or a downloadable program, then the answer is yes, and only the client-side ID belongs there. If the code runs inside a server you control, and the string is loaded from protected storage, the server-side key is both allowed and appropriate. When in doubt, the safer choice is always the one with fewer powers.
The dangerous mistake is one-directional. Using the client-side ID on a server is merely a missed opportunity, since you lose local evaluation. Using the server-side key in a client is a leak. If you ever have to guess, guess the public one.
Myths and facts
| Common belief | The reality |
|---|---|
| “All environment keys are secrets” | Only the server-side key is. The client-side ID is public by design |
| “The client-side ID is useless to attackers, so exposure is irrelevant” | It is low risk, but the values it can read and the traits it may send are still worth designing carefully |
| “A leaked server-side key lets attackers switch my flags” | It lets them read your rules and values; changing flags needs a separate management credential |
| “One key per project is enough” | Keys are per environment, and one key per consumer is safer |
| “Deleting the key from the repository fixes the leak” | The key remains in history; it must be replaced |
| “A private repository is a safe place for a secret” | Private repositories can be reached with stolen logins, copied to laptops, and forked; use a secrets manager |
| “Using the server-side key everywhere is simpler and therefore fine” | Simpler for you, and dangerous in any code a user can inspect |
CWhere Keys Live in Flagsmith’s Structure
Keys belong to environments, and environments belong to projects. Understanding this hierarchy prevents a whole family of mix-ups.
Flagsmith organises everything in layers. At the top is your organisation, usually your company. Inside it live projects, typically one per product or application. Inside each project live environments, such as development, staging, and production. Inside each environment live your features, with their states, values, segments, and overrides. Keys sit at the environment layer. This is deliberate: it means the environment, and not the project or the organisation, is the unit of access.
Organisation
Your company account. Users, roles, and billing live here. It has no environment keys of its own, but it can hold separate administrative credentials.
Project
One application or product. It holds the list of features and segments that all its environments share as definitions.
Environment
One stage of life, such as production. It owns its own flag values and its own keys. This is where SDKs connect.
Feature
A named flag or value. Its state differs in each environment, which is why keys must be per environment.
What “one environment per key” means in practice
A key for production gives access to production flags only. A key for staging gives access to staging flags only. Even if the two environments contain a feature with the same name, a production key can never read the staging value, and a staging key can never read the production value. This isolation is the reason you can safely let a developer experiment freely in a development environment: the experiment cannot reach production, because the development keys do not point there.
flowchart TD Org["Organisation"] --> P1["Project: Shop Web App"] Org --> P2["Project: Mobile Backend"] P1 --> E1["Environment: Development"] P1 --> E2["Environment: Staging"] P1 --> E3["Environment: Production"] E3 --> K1["Client-side environment ID"] E3 --> K2["Server-side key: checkout service"] E3 --> K3["Server-side key: reporting job"] E1 --> K4["Its own client-side ID and server-side keys"]
Picture a hotel group with several hotels, each with several floors. A master key for the fourth floor of one hotel opens only those rooms and nothing else in the group. The group does not give out one key that opens every door in every hotel, because one lost key would then be a disaster. Environment keys follow the same discipline: small territory, small damage.
Because keys belong to environments, the same mistake can happen quietly in a different shape: a staging service accidentally configured with a production key. It will run, and it will read real production flags. Check which environment a key points at before you deploy, not after.
Counting keys: how sprawl happens
Key counts grow faster than most teams expect, because they multiply across layers. An organisation with 6 projects, 3 environments each, and 4 server-side keys per environment already has 72 server-side keys, plus 18 client-side IDs. Add temporary environments for pull requests, a handful of experimental sandboxes, and keys for the Edge Proxy, and the total can pass a few hundred within a year. Nobody planned this; it simply accumulates, one reasonable decision at a time.
Sprawl is dangerous because forgotten keys are unmonitored keys. A key that nobody remembers is a key nobody rotates, nobody revokes when a team is disbanded, and nobody would notice being used by a stranger. The remedy is cheap: record every key in an inventory with an owner, review the inventory each quarter, and delete anything without a living owner or a known purpose. Deleting an unused key is a small, safe action, and a short list is far easier to defend than a long one.
DThe Client-Side Environment ID in Depth
It is long, random, and public on purpose. Here is exactly what the client-side ID is, and why exposing it is acceptable.
What it is
The client-side environment ID is a public identifier for one environment. Think of it as the environment’s name tag. When a browser or mobile app starts the Flagsmith SDK, it supplies this ID. Flagsmith reads it and knows which environment’s flags to prepare. The ID is placed into the front-end code, which means that anyone who opens the page’s developer tools, or unpacks the app, can read it. The design assumes this will happen.
What it can do
With only the client-side ID, a caller can ask for the flags that apply to one identity, along with those flags’ on/off state and values. This is what a front end needs: “show me what this particular user should see.” Depending on how the environment is configured, a caller can also send traits for that identity, so that segments can target by them. Those two abilities are all the front end requires.
What it cannot do
Download the rulebook
The complete environment document, with every segment rule and override, requires the secret key. The public ID is refused.
Edit flags
It cannot create, change, or delete features, segments, or environments. Management is a separate credential entirely.
Run local evaluation
Local evaluation needs the full document, so it needs the server-side key. Client SDKs always rely on the server to decide.
Reach other environments
It points at one environment only. Staging and development remain out of reach.
Why public is acceptable
Three ideas make exposure tolerable. First, the ID only identifies; it does not unlock management or bulk data. Second, the answers a client can obtain are limited to what a front end legitimately needs: the flags for the identity being asked about. Third, any feature a user can see in the interface will necessarily be visible to that user anyway, because the interface must behave differently. Hiding the flag state from the person whose screen it controls is neither possible nor useful.
“Safe to expose” does not mean “free of consequences”. Because anyone can use the public ID, anyone can ask what flags apply to any identity label they invent, and anyone may be able to send traits. A later chapter on exposure risks explains what that means for your design. The short version: never put secrets inside flag values that a client can read.
Where you will see it
A single-page web application initialises the Flagsmith SDK with the client-side ID in its JavaScript bundle. A mobile app does the same in its configuration. A marketing site loads it into a small script. In each case the ID travels to the user’s device, and that is fine, as long as nothing secret travels with it.
How a client SDK uses the ID over time
Following the ID through the life of a client application shows how it behaves in practice. When the application starts, the SDK is given the ID and, usually, an identity for the current user. It sends a request carrying the ID and asks for that identity’s flags. Flagsmith recognises the ID, finds the environment, evaluates the rules for that identity on its side, and returns the results. The SDK stores them in memory, and sometimes on the device, so the interface can read them instantly without waiting for the network again.
During the session, the application may update the identity’s traits, for example after the user changes their plan, and ask again. Each request repeats the same ID. Some client SDKs also support receiving updates when flags change, so the interface can react without a reload; others simply refresh on demand or at intervals. In every case, the ID is a constant companion that tells Flagsmith which environment the questions are about, while the user’s identity, which changes, tells it who they are about.
Initialise
The SDK receives the ID and the user’s identity and requests the first set of flags.
Read from memory
The interface reads flag values from the SDK’s stored results, quickly and without further requests.
Refresh on change
When traits change or the app returns to the foreground, the SDK asks again using the same ID.
Session closes
The stored results disappear with the page or are cached on the device, depending on the SDK and settings.
EThe Server-Side Environment Key in Depth
It looks like the public ID, but it is a different thing altogether: a secret that unlocks more power and demands more care.
What it is
The server-side environment key is a secret credential for one environment. You create it in the dashboard, in the environment’s SDK keys area, and you can create several, each with a name of your choice. The dashboard shows the full value at creation, and you should copy it straight into a secrets manager. Server-side keys typically carry a recognisable prefix, which helps both humans and automated scanners spot them. Check the current documentation for the exact prefix in use.
What it unlocks
Evaluate flags remotely
Ask Flagsmith for the flags that apply to an identity, exactly as a client could, with the trust level of a backend.
Download the environment document
Fetch the entire rulebook for the environment, which enables local evaluation inside your own process.
Send usage analytics
Report how often flags are evaluated, so the dashboard can show which flags are alive.
Manage flags
It cannot edit feature definitions, segments, or environments. Those need a human login or an administrative token.
Why it is the sensitive one
Remember what the environment document contains: every feature, every segment with its rules, every percentage split, and, where supported, every identity override. With the server-side key, a single request returns all of it. An outsider holding the key could learn which customers are beta testers, how pricing tiers are targeted, which unreleased features are waiting, and how experiments are split. The key does not let them change anything, but knowledge of your targeting logic is itself valuable, and a leaked key is a stubborn problem because it keeps working until someone deletes it.
Imagine a restaurant’s recipe book, with every secret ingredient and every supplier price. A customer who is shown one dish, which is the client-side experience, learns nothing about the book. A person who photographs the whole book, which is what the server-side key allows, learns everything. They cannot cook in the restaurant’s kitchen, since they cannot change the real recipe book, but a competitor would very much like that photograph.
Many keys, many names
You are not limited to one server-side key per environment, and that is a feature. Create one per service, or one per team, and give each a descriptive name such as “checkout-service-prod”. Separate keys let you trace which service uses which key, revoke a single key without disturbing the others, and rotate keys one service at a time. A single shared key for the whole company is convenient on day one and painful on the day it leaks.
Name keys after the consumer and the environment, not after the person who created them. People leave and change roles; services and environments endure. A name that says “reporting-job-staging” is useful for years, while one that says “dave-test” is a mystery within a month.
The life of a key from birth to deletion
Every server-side key passes through a lifecycle, and managing it deliberately at each stage is the heart of good key hygiene. It is born when someone creates it in the dashboard, and the full value is shown. It is placed in a secrets manager and linked to the consumer that will use it. It is used every time the service starts, and, in local evaluation, every time the poller refreshes. It is watched by metrics that show authentication failures and by scanners that look for stray copies. It is rotated on a schedule or after an event. Finally it is retired: deleted from Flagsmith and removed from the secrets manager.
The stages most often forgotten are the last two. Teams create and use keys diligently and never retire them. A service is decommissioned, but its key lives on. A contractor leaves, but a key they once copied is still valid. Make retirement a standard step in your decommissioning checklist: when a service is shut down, its keys are deleted the same day. Treat any key without an active consumer as a liability.
Born
Created with a descriptive name in the correct environment.
Placed
Stored in the secrets manager, with narrow access for one consumer.
Used and watched
Read at startup and monitored for failures and stray copies.
Rotated
Replaced on a schedule or after any event that could have exposed it.
Retired
Deleted in both Flagsmith and the secrets manager when its consumer disappears.
FArchitecture: Which Key Goes Where
A simple map of a typical system shows where each key belongs and where it must never appear.
The diagram below shows a typical product. On the left are the places that users can inspect: a web page in a browser and a mobile app on a phone. These use the public client-side ID. In the middle are your own backend services, running inside your infrastructure. They use server-side keys, loaded from a secrets store at startup. On the right is Flagsmith itself, which reads the key on every request to decide which environment is meant and how much trust to extend.
flowchart LR
subgraph Untrusted["Places users can inspect"]
Web["Web browser"]
Mob["Mobile app"]
end
subgraph Trusted["Your protected infrastructure"]
API["Backend API service"]
Job["Background worker"]
SM["Secrets manager"]
end
Web -- "client-side ID" --> FS["Flagsmith"]
Mob -- "client-side ID" --> FS
SM --> API
SM --> Job
API -- "server-side key" --> FS
Job -- "server-side key" --> FS
Admin["Engineer using the dashboard"] -- "login and roles" --> FS
The three kinds of caller
| Caller | Credential | What it is allowed to do |
|---|---|---|
| A browser or mobile app | Client-side environment ID | Read flags for an identity; possibly send that identity’s traits |
| A backend service or job | Server-side environment key | Read flags, download the environment document, report analytics |
| A human engineer or an automation that manages flags | Dashboard login, or an administrative token | Create and edit features, segments, and environments, within role limits |
Notice that the third caller uses a completely different credential. This separation is important: environment keys are for applications that consume flags, not for people or tools that manage them. If you find yourself wanting to use an environment key to change a flag, you are reaching for the wrong tool, and the answer is an administrative credential with its own permissions.
Backends sit in the trusted zone, but trust is not infinite. Containers get compromised, logs get shared, and engineers copy values into chat messages. The architecture limits the damage, and good habits, covered later, reduce the chance.
GAnatomy of an Authenticated Request
What actually happens, step by step, when an SDK presents a key? Following one request makes the whole security model concrete.
Every call from an SDK to Flagsmith carries the key in a request header, which is a small label attached to the message, like the address written on an envelope. Flagsmith reads the header first, before looking at anything else. From the key it learns two things at once: which environment the caller means, and which kind of key it is, which decides what the caller is permitted to do.
sequenceDiagram
participant S as SDK
participant F as Flagsmith Edge API
S->>F: Request with the key in a header
F->>F: Look up the key and find the environment
F->>F: Decide the key type and its permissions
alt Key unknown or revoked
F-->>S: Rejected as unauthorised
else Key allowed for this action
F->>F: Evaluate or prepare the response
F-->>S: Flags or the environment document
else Key valid but action not allowed
F-->>S: Rejected as forbidden
end
Three possible outcomes
Accepted
The key is known, belongs to a live environment, and its type permits the action. The caller gets its flags, or, with a server-side key, its document.
Unauthorised
The key is unknown, mistyped, or has been deleted. Flagsmith cannot tell who is asking and refuses. Typical causes are copy-and-paste errors, a revoked key, or a key from a different Flagsmith installation.
Forbidden
The key is valid, but it is the wrong kind for what was asked. The classic example is trying to download the environment document with the public client-side ID.
Reading the errors
These outcomes show up in your logs as familiar numeric codes, and learning to read them saves hours. An unauthorised result means “I do not recognise you”. Look for typos, trailing spaces, quotes accidentally included, a deleted key, or a key for the wrong installation. A forbidden result means “I recognise you, but you may not do that”. Look for the wrong key type, such as a client-side ID in a place that needs the server-side key. A sudden burst of either, from a service that worked yesterday, points to a recent key change or a deployment that picked up the wrong value.
A nightclub bouncer checks two things. First, is the ticket genuine and for tonight? If not, you are turned away at the door. Second, is it a general ticket or a backstage pass? A general ticket gets you to the dance floor; only the backstage pass opens the staff corridor. A general ticket held up at the staff corridor is not forged, it is simply not enough, and that is the difference between “unauthorised” and “forbidden”.
When debugging authentication problems, confirm in this order: is the key present, is it the exact string from the dashboard with no extra characters, does it belong to the environment you intend, and is it the right type for the SDK and mode you are using? Four checks, in that order, resolve almost every case.
Protecting the key in transit
A key must travel from your service to Flagsmith, and the journey needs protection. SDK traffic should always use an encrypted connection, known as TLS, which scrambles the contents so that anyone listening on the network sees only noise. The key lives in a request header, and encryption covers headers as well as content. Make sure your SDK is configured with a secure address, beginning with the secure web prefix, and that any internal proxy you pass through is configured not to downgrade the connection.
The less obvious danger is what intermediate systems record. Corporate proxies, load balancers, and observability agents sometimes log request headers for debugging. If they log the header that carries your key, the key is now sitting in a log system with many readers and long retention. Check what your network and monitoring tools capture, and configure them to redact the key header. Do the same for any tool that records outgoing requests from your services, such as tracing or debugging proxies.
“Verbose” or “debug” modes in HTTP libraries frequently print every header of every request. Never leave such modes on in production, and never paste their output into tickets or chats without first removing the key.
HChoosing the Right Key for Your Runtime
The right key depends on one thing: who can read the code that holds it. A short decision path settles it every time.
flowchart TD
A["Where does the SDK run?"] --> B{"Can a user open or unpack this code?"}
B -- Yes --> C["Use the client-side environment ID only"]
B -- No --> D{"Do you need local evaluation or the full rulebook?"}
D -- Yes --> E["Use a server-side key from a secrets manager"]
D -- No --> F{"Is the environment variable or file protected?"}
F -- Yes --> G["Server-side key is allowed, and preferred for backends"]
F -- No --> H["Fix the storage first, or use the client-side ID"]
| Runtime | Right key | Reason |
|---|---|---|
| Web page or single-page application | Client-side ID | Anyone can read the bundle and network calls |
| Mobile app (iOS, Android, cross-platform) | Client-side ID | Apps can be unpacked and inspected by users |
| Desktop software shipped to customers | Client-side ID | Distributed binaries can be examined |
| Backend API in your cloud | Server-side key | Code is private; local evaluation is available |
| Background worker or scheduled job | Server-side key | Runs in your infrastructure with protected configuration |
| Serverless function | Server-side key, injected securely | Private runtime; use the platform’s secret storage |
| Test suite in your CI pipeline | Test environment key, or offline mode | Avoid production keys in tests entirely |
| Server-rendered web app (rendering done on your server) | Server-side key on the server; client-side ID for the browser part | Each side uses what its audience may see |
Backends that serve front ends
A common design is a front end that needs a few flag decisions, supported by a backend that already knows who the user is. There are two good ways to give the front end its flags. The first is to use a client SDK with the public client-side ID and let it ask Flagsmith directly. The second is to let the backend evaluate the flags with its server-side key and send the needed results to the front end as part of its normal response. The second approach keeps targeting rules, trait handling, and secrets entirely on your side, and is attractive when flag values contain anything you would rather not expose.
Some server-side SDKs can also be started with the public client-side ID, in which case they work only in remote evaluation mode. This is allowed, and sometimes useful for simple scripts, but you give up local evaluation. For real services, prefer the server-side key.
Hybrid applications and tricky cases
Real products rarely fit the neat table. A server-rendered web page is built on your server, but then runs in the browser. A desktop application is distributed to customers but talks to your backend. A mobile app may need flags before the user signs in. For each case, apply the same test: where does each line of code actually run, and who can read it there? Server-rendering code can use the server-side key, because it runs privately, but any flag logic that is shipped to the browser afterwards must use the client-side ID, or receive only the results already evaluated on the server.
Edge and serverless platforms deserve a second look. A function running at the edge of a content network is still code you deploy, so it can hold a server-side key, but many such platforms make environment variables visible to every engineer with deployment access. Check who can read those variables, and prefer the platform’s secret storage over plain settings. For desktop software, assume anyone can unpack the program: use only the client-side ID, and send sensitive decisions through your own authenticated backend.
A mobile app before sign-in
A retail app wants to show a seasonal banner on its launch screen, before anyone has signed in. It uses the client-side ID and an anonymous identity, such as a random installation identifier, to fetch the banner flag. After sign-in, it switches to the user’s opaque identifier, so targeting by plan or region becomes possible. At no point does the app contain a server-side key, and all sensitive decisions, such as pricing, are evaluated by the backend.
IExposure Risks of the Public Client-Side ID
The client-side ID is meant to be public, but “public” is not “harmless”. A careful designer knows exactly what the ID reveals.
Risk one: flag values are visible
Whatever the client SDK can read, a determined person can read, with or without your application. Anyone with the ID can ask what the flags and values are for an identity. This means that remote config values reachable by client SDKs must contain nothing secret. A value that says “show the new banner” is fine. A value that contains an access token, a private URL, or an internal price list is a leak waiting to be found. Keep secrets out of flag values altogether, and keep sensitive configuration in your own secured systems.
Risk two: anyone can invent an identity
The identity label is just text. A curious visitor can ask Flagsmith for the flags of any identity label they like, including labels belonging to real users, if those labels are guessable. If your identity labels are sequential numbers, a visitor can walk through them. Using opaque, unguessable identifiers instead of email addresses or small sequence numbers makes this guessing pointless, and prevents the labels from leaking personal information when they appear in network traffic.
Risk three: segment probing
If a client is allowed to send traits, a curious person can send different traits and watch how the answer changes. By trying “plan equals enterprise”, then “country equals DE”, and so on, they could work out the logic of your segments. This reveals your targeting design rather than breaking anything, but it is a real exposure for businesses that treat pricing tiers or experiments as confidential. Settings exist in the environment to limit what client SDKs may do, and the safest design for sensitive targeting is to evaluate on your server with the secret key, and send only results to the client.
Risk four: noise and abuse
Because the ID is public, someone can send large numbers of requests with it. That can consume your request allowance or create clutter in identity lists. Rate limiting at the platform, plus sensible monitoring on your side, keeps this manageable. For most organisations this is a theoretical annoyance rather than a daily problem, but it is worth knowing about.
No secrets in values
Treat every client-readable flag value as if it were printed on the page.
Opaque identifiers
Use random internal identifiers as identities, never emails, names, or guessable numbers.
Hide disabled flags
Environment settings let you stop disabled flags being returned to SDKs, so unreleased features do not leak their names.
Evaluate sensitive logic server-side
Send only the final result to the browser, and keep the rules and traits in your backend.
Flag names themselves can leak plans. A flag called “launch_acquired_competitor_integration” returned to every browser tells the world what you are building. Use neutral, boring names for sensitive unreleased work, and enable the setting that hides disabled flags from SDK responses.
Putting it together
A fintech wants to roll out a new fee schedule by customer tier. The tier rules and percentage splits are confidential. Instead of sending traits from the browser, the backend uses its server-side key, evaluates locally with the customer’s tier, and returns a single decision to the page: “show schedule B”. The browser never learns the rules, never sees the traits, and never needs more than a boolean. The client-side ID is still used for harmless, public features such as a new button style.
Questions to ask before exposing a flag to clients
Before a flag becomes readable by client SDKs, a quick set of questions prevents most exposure problems. Would I be comfortable if this flag’s name and value appeared in a newspaper? Does the name hint at an unreleased product, a customer, or a partnership? Does the value contain anything that could be misused, such as an address, a token, or a threshold that reveals a business rule? Does a segment targeting this flag rely on traits that a visitor could guess and try? If any answer is uncomfortable, move the decision to the server and send only the outcome.
| Flag type | Safe for clients? | Better design |
|---|---|---|
| Show or hide a new button | Yes | None needed |
| Choose a colour theme variant | Yes | None needed |
| Name hints at an unreleased product | Risky | Use a neutral name and hide disabled flags |
| Value holds an internal address or token | No | Keep the secret in your backend and flag only the behaviour |
| Pricing or discount rules by customer tier | Risky | Evaluate on the server and return only the result |
| Permission to use a paid feature | Only as a hint | Always enforce again on the server |
JWhen a Server-Side Key Leaks: The Blast Radius
Security people talk about “blast radius”: how much damage one mistake can do. Knowing the radius of a leaked server-side key tells you how urgently to react.
What an attacker gains
Holding a server-side key, an outsider can download the environment document and read the full targeting rulebook for that one environment. They can see segment definitions, identity overrides, percentage splits, flag values, and the names of every feature, including ones not yet released. They can also ask Flagsmith to evaluate flags for any identity, and may be able to send analytics events, which could pollute your usage statistics.
What an attacker does not gain
The key is a consumer credential. It does not let the holder edit features, change segment rules, switch flags on or off, add team members, or touch other environments. This is a vital limit. A leaked key exposes knowledge, but it does not hand over control of your production behaviour. People sometimes panic on the wrong grounds, imagining that an intruder could flip flags and break production. That is not what this key allows; for that, an attacker would need a dashboard login or an administrative token, which are guarded separately.
| Question | Leaked server-side key | Leaked client-side ID |
|---|---|---|
| Can they read your targeting rules? | Yes, in full | No, only per-identity results |
| Can they read flag values? | All of them | Those returned for an identity |
| Can they change a flag? | No | No |
| Can they reach other environments? | No | No |
| Is it urgent to replace? | Yes, promptly | Rarely |
| Main harm | Disclosure of business logic and unreleased plans | Minor noise; limited data exposure by design |
Indirect consequences worth thinking about
The harm from knowing your rulebook can still be serious. A competitor learning about an unreleased feature, a journalist discovering a pricing experiment, or a malicious user finding the exact conditions under which a safety check is disabled: all are realistic concerns. Rules that mention specific customer identifiers in overrides could also reveal who your beta customers or key accounts are. And where flags guard security-relevant behaviour, such as a weaker check for internal testers, knowing the rule helps an attacker design an attack that fits it.
Imagine someone steals the keys to a cinema’s projection booth. They cannot rewrite the film or change what the audience sees tonight, but they can read the schedule for next month’s unreleased premieres and the guest list for the private screenings. Annoying, possibly costly, but different in kind from someone taking over the projector. Still, you would change the locks the same day.
“It cannot change flags” is not a reason to relax. A key that keeps working forever, hidden in an old repository, will eventually be found by someone you did not choose. Treat every confirmed exposure as an incident, and replace the key.
Estimating the damage in your own organisation
The right level of urgency depends on what your rulebook contains. Walk through a short assessment for the affected environment. How many features does it hold, and are any unreleased or commercially sensitive? Do segment rules encode pricing, eligibility, or fraud checks? Do identity overrides name individual customers or staff? Do any flags weaken a safety or security check for testers? A rulebook full of harmless interface experiments is a small disclosure; one that encodes confidential pricing or security exceptions is a large one, and may justify informing other teams or leadership.
Write the answers down as part of the incident record. They guide the response and also serve as a useful design review. If the assessment shows that sensitive rules sit in an environment read by many services, consider splitting them: keep confidential targeting in a separate project with fewer consumers, so a leak of one key reveals less. Smaller rulebooks with fewer holders are naturally safer, and this is one of the quiet benefits of keeping projects focused.
KEnvironment Keys vs Administrative Credentials
Flagsmith has more than one kind of secret. Keeping them straight prevents both over-powered applications and under-powered automation.
Environment keys, client-side or server-side, exist so that applications can read flags. A separate family of credentials exists so that people and tools can manage them. These include the dashboard login of each user, protected by whatever sign-in method your organisation uses, and administrative tokens or API keys that scripts and pipelines can present to the management interface. The permissions are different, the storage rules are different, and mixing them up in either direction is a design error.
| Credential | Used by | Purpose | Power |
|---|---|---|---|
| Client-side environment ID | Browsers, mobile apps | Read flags for an identity | Lowest, public |
| Server-side environment key | Backend services | Read flags and the environment document | Medium, secret |
| Dashboard login | People | Manage flags within assigned roles | Role-based, personal |
| Administrative token or API key | Automation and scripts | Manage flags, environments, and settings programmatically | High, secret, tied to a user or an organisation |
Two common design errors
Management credentials in applications
Putting an administrative token inside a service so it can “just check a flag” gives that service the power to change everything. Applications need environment keys, not management power.
Environment keys in management tools
Trying to use a server-side key in a script that creates flags will be refused, as it should be. Use a proper administrative credential, stored just as carefully.
Separate lifecycles
Environment keys rotate with deployments. Administrative tokens rotate with staff changes and tooling changes. Keep the two lifecycles apart.
Least privilege everywhere
Give each credential only the smallest set of powers its job needs, and name it so its purpose is obvious.
The exact names and options for administrative credentials depend on your plan and version, so read the current documentation for the management interface. The principle is stable: management credentials are more powerful than environment keys and deserve at least as much care.
An automation example
A team wants a nightly job that creates a flag in staging whenever a feature branch is opened. The job needs to create features, which is management, so it uses an administrative token stored in the CI system’s secret store. The staging application, which merely reads the new flag, uses its own staging server-side key. Two credentials, two purposes, and neither can do the other’s job.
LCreating and Naming Keys
Good key hygiene starts at creation. A few minutes of care here saves hours during an incident.
Decide the consumer
Which service or job will use this key, and in which environment? One key should serve one consumer. If two services need access, create two keys.
Open the right environment
In the dashboard, select the project and the environment first. Double-check the environment name, since a key created in the wrong place works perfectly and silently.
Create with a meaningful name
Use a pattern such as service, then environment, for example “checkout-prod”. Add a short note if the interface allows, explaining the owner and purpose.
Copy straight into the vault
Paste the value directly into your secrets manager. Do not paste it into chat, email, tickets, or a notes file, even briefly.
Grant narrow access
Allow only the identity of the consuming service to read the secret. Developers do not need to read production values to deploy.
Record the key in an inventory
Write down the key’s name, owner, purpose, creation date, and where it is stored. Never record the value itself in the inventory.
A naming convention that lasts
Choose a consistent pattern and document it. A good pattern contains three pieces of information: the consumer, the environment, and, where you rotate keys, a generation marker. For example, “checkout-api-prod-2026a” tells a future engineer which service uses it, which environment it points at, and that it is the first key of 2026. When you rotate, the new key becomes “checkout-api-prod-2026b”, and the old one is easy to identify and delete.
Consumer
The service, job, or pipeline that uses the key, so you know whom to talk to before revoking it.
Environment
Production, staging, or development spelled out, to catch mix-ups when reading logs and configurations.
Generation
A simple letter or date so old and new keys can coexist during rotation without confusion.
Personal names and “test”
Names such as “dave-test” or “temp” tend to become permanent, and nobody dares to delete them.
Create a separate key for each developer-facing sandbox, scoped to a development environment. When a laptop is lost or a developer leaves, you can delete their sandbox key without touching anything that real users depend on.
Who may create and see keys?
Not everyone with dashboard access needs the ability to create or reveal server-side keys. Use the roles and permissions available in your plan to limit this power to a small group, such as platform and security engineers. Developers may need to see the client-side ID, which is public, but have little reason to view production server-side keys. If your process requires a developer to create a key, use a short, documented request flow, so every key has an owner and a record from the start. Keeping creation rights narrow also narrows the number of people who could ever have seen a secret, which makes investigation much simpler after an incident.
Make “create a key” a small, repeatable procedure, with a template for the name, a place to record the owner, and a checklist for storage. A boring, standard process produces consistent keys, and consistent keys are far easier to audit, rotate, and revoke.
MStoring Keys Safely
A secret is only as safe as the place where it sleeps. Here are the good places, the poor places, and the dangerous ones.
The golden rule of secrets is that they should never be written into anything that travels: source code, container images, build artifacts, documents, chat messages, or screenshots. Instead, they are kept in a dedicated store and delivered to the running service at the last possible moment, in memory, through an identity that the platform controls.
| Where | Verdict | Why |
|---|---|---|
| A managed secrets service such as AWS Secrets Manager | Best | Encrypted at rest, access controlled by identity, audited, supports rotation |
| A parameter store with encrypted values | Good | Simple and cheaper, with access control and encryption when configured properly |
| A vault product run by your team | Good | Powerful and flexible, but you operate it, so availability and security are yours |
| Orchestrator secrets, such as Kubernetes Secrets | Acceptable with care | Values are encoded, not necessarily encrypted, unless encryption at rest and tight access are set up |
| CI system secret store | Good for pipelines | Keeps secrets out of repositories and masks them in logs when used correctly |
| Environment variable set by the platform from a secret store | Good | The usual way a service receives its key at start |
| A configuration file in the repository | Never | Spreads to every clone, fork, and backup |
| A container image or build artifact | Never | Anyone who can pull the image can read the key |
| Chat, email, tickets, wikis | Never | Unlimited copies, long retention, many readers |
How the key travels to the service
sequenceDiagram participant D as Deployment platform participant SM as Secrets manager participant T as Service task participant F as Flagsmith D->>T: Start task with its own identity T->>SM: Ask for the Flagsmith key using that identity SM->>SM: Check the task identity may read this secret SM-->>T: Return the key into memory T->>F: Start SDK and make requests with the key Note over T,SM: The key never appears in code, images or logs
Local development
Developers need keys too, but they do not need production keys. Give each developer, or each shared sandbox, access to a development environment with its own keys. Keep those in a local file that is excluded from version control, or in a developer-friendly secrets tool. Make sure the project’s ignore rules cover such files, and add an automated check that blocks commits containing key-shaped strings. Production keys should be available only to production workloads, never to individual laptops.
Deleting a secret from the latest version of a repository does not remove it from history. Anyone who clones the repository can still find the old commit. If a key has ever been committed, treat it as leaked and replace it, whatever you do to the code afterwards.
Containers, orchestrators and pipelines
Container platforms add special cases. Images are shared widely and cached on many machines, so a key must never be baked into an image; it should arrive when the container starts. Orchestrators such as Kubernetes offer built-in secret objects, but by default these are merely encoded, not strongly protected, so enable encryption at rest, restrict who can read them with access rules, and consider a tool that synchronises values from a real secrets manager into the cluster at runtime. Prefer mounting a secret as a file or injecting it as an environment variable at start, and avoid placing it in a configuration map, which is designed for non-sensitive values and is often more widely readable.
Continuous integration pipelines need keys too, for tests and deployments, and they are a favourite target for attackers, because they run code from many contributors. Store pipeline secrets in the pipeline system’s own secret store, scope each secret to the jobs and branches that truly need it, and never expose secrets to builds triggered from untrusted forks. Use test-environment keys in test jobs, and keep production keys available only to the deployment job that actually needs them. Turn on masking so that accidental printing of a secret appears as asterisks in logs.
Inject at start
Pass the key into the container when it starts, never into the image build.
Encrypt and restrict
Enable encryption at rest for cluster secrets and limit read access to the services that need them.
Scope narrowly
Expose a secret only to the job and branch that needs it, never to builds from untrusted forks.
Mask outputs
Ensure secrets are masked in logs, and avoid commands that echo environment variables.
NFramework and Build-Tool Pitfalls
Many leaks happen not through carelessness, but through a build tool doing something helpful that nobody noticed.
Modern front-end frameworks make it easy to use environment variables in your code. To prevent accidental exposure, most of them follow a convention: only variables with a special prefix are copied into the browser bundle, and everything else stays on the server. Conventions of this kind include prefixes such as REACT_APP_, NEXT_PUBLIC_, and VITE_. The prefix means “this value is allowed to be public”. The danger is easy to see: if a developer names the server-side Flagsmith key with a public prefix, the build tool dutifully places it in the downloadable bundle, and the secret ships to every visitor.
Public prefix on a secret
A secret named with a “public” prefix is copied into the front-end bundle by design. The tool is working correctly; the name was wrong.
Server code in the browser bundle
Frameworks that mix server and browser code can include a module in the wrong place if imports are careless. Check which files end up in the shipped bundle.
Source maps
Published source maps can make it easy to read the original code, including any value that was compiled in.
Mobile app configuration
Anything inside an app package can be extracted. A secret in the app’s settings file is a secret that every user can read.
A safe way to name things
Adopt a naming scheme that makes the type of key obvious from its variable name. For example, name the public one with the word “public” or “client” in it, and the secret one with the word “secret” or “server”. A reviewer reading a change can then spot an error at a glance: a variable with “secret” in its name and a public prefix is a red flag. Combine this with automated checks that scan the built front-end bundle for the secret key’s prefix before every release.
Name by sensitivity
Names should say whether a value is public or secret, so mistakes are visible in code review.
Separate front-end and back-end settings
Keep the two in different files and different pipelines, so a secret has no path into a browser build.
Scan the build output
After building, search the produced files for the server-side key prefix and fail the build if it appears.
Review the network traffic
Open the browser’s developer tools on a test build and check which keys appear in requests. Only the public ID should be there.
A quick manual test works wonders: load your own site, open the page source and developer tools, and search for the first characters of your server-side key’s prefix. If the search finds anything, you have just saved yourself an incident.
ORotating Keys Without Downtime
Rotation means replacing a key on purpose, before anyone forces you to. Done well, nobody notices; done badly, production goes dark.
Rotation sounds frightening because the obvious way to do it is dangerous: delete the old key and hope the new one is deployed in time. Flagsmith’s support for several server-side keys per environment makes a much safer way possible. You create the new key first, while the old one still works. You deploy the new key to every consumer. Only when every consumer has switched do you delete the old one. At every moment, every service has at least one working key.
sequenceDiagram participant Ops as Engineer participant FS as Flagsmith Dashboard participant SM as Secrets manager participant Svc as Service instances Ops->>FS: Create a new key for the same environment Ops->>SM: Store the new key as the next version Ops->>Svc: Roll out so instances load the new key Svc->>FS: Requests now use the new key Ops->>FS: Confirm the old key is no longer used Ops->>FS: Delete the old key Ops->>SM: Remove the old secret version
The rotation steps in plain words
Create the new key
Add a second server-side key to the same environment, named with the next generation marker.
Store it
Put the new value in the secrets manager as a new version, leaving the old version in place for the moment.
Roll it out
Restart or redeploy the consuming services so they read the new value. Roll gradually and watch for authentication errors.
Verify
Confirm from your own metrics that no instance still uses the old key. Look at deployment versions and at the document refresh success rate.
Delete the old key
Remove the old key in the dashboard and the old version in the secrets manager. Rotation is complete only when the old key is gone.
How often?
There is no single right schedule. A common starting point is once or twice a year for routine rotation, and immediately after any suspected exposure, any departure of someone with access to production secrets, or any change of tooling that touched the storage. What matters more than frequency is practice. A team that has rotated a key calmly three times will do it well in an emergency. A team that has never done it will discover its hidden dependencies, such as a forgotten script with the old key pasted in, at the worst possible moment.
Long-running processes that read their key at startup will not notice a new value until they restart. Local evaluation clients keep using the key they started with. Plan the rollout so that all instances restart before you delete the old key.
If your secrets manager supports scheduled rotation, you can automate the storage side. You still need a deliberate step that creates the new key in Flagsmith and removes the old one, so write that step as a small, tested script using an administrative credential, and keep it in your runbook.
How rotations go wrong
Most rotation failures come from a small set of causes, all avoidable. The first is the forgotten consumer: a script, a cron job, or a staging service still using the old key, which breaks the moment it is deleted. Avoid this by naming keys per consumer, so the old key’s name tells you exactly whom to check, and by watching authentication failures after each step. The second is the stale process: a long-running service that read the old key at startup and never restarted, so it keeps using a key you are about to delete. Avoid it by restarting every consumer and confirming from deployment records that none was missed.
The third is deleting too early: removing the old key the moment the new one is created. The fourth is caching in unexpected places: a secrets cache, a sidecar, or a configuration service that keeps handing out the old value for a while. Each of these is easy to fix once you know it exists. The surest way to find them is a rehearsal in a safe environment, where a failure costs a little time and nothing else.
| Failure | Symptom | Prevention |
|---|---|---|
| Forgotten consumer | A job or service starts failing after deletion | One key per consumer; check failures by key name before deleting |
| Stale process | Errors appear hours later when an old process refreshes | Restart everything and verify versions |
| Deleting too early | Immediate authentication errors across services | Delete only after verification |
| Cached secret | New key stored, old key still served | Know your caches and clear them as part of the plan |
| Wrong environment | Staging breaks or production reads staging flags | Name keys with environment; log it at startup |
PIncident Response: When a Key Is Exposed
Nobody plans to leak a key, but everybody should plan for it. A short, rehearsed runbook turns panic into procedure.
flowchart TD
A["Suspected or confirmed exposure"] --> B{"Which key type?"}
B -- "Client-side ID" --> C["Low urgency: review exposed values and settings"]
B -- "Server-side key" --> D["Create a replacement key now"]
D --> E["Deploy the replacement to all consumers"]
E --> F["Delete the exposed key"]
F --> G["Review the audit log and access records"]
G --> H["Assess what the rulebook revealed"]
H --> I["Fix the cause and write the lessons down"]
C --> I
The first hour
Confirm what leaked
Identify the exact key, its environment, and where it was exposed. Is it a client-side ID, which is low risk, or a server-side key, which is not?
Create a replacement first
Make a new key and deploy it to all consumers, so that deleting the old one will not cause an outage.
Delete the exposed key
Revoke it in the dashboard. From that moment, anyone holding the old value is refused.
Clean up the exposure
Remove the key from the place where it leaked, such as a repository, a ticket, or a log, remembering that history and copies may persist.
Check for changes
Review the audit log for any unexpected flag or setting changes, in case a separate credential was also exposed.
The next days
After the urgent steps, ask what the rulebook revealed. Were there overrides naming specific customers? Were there unreleased features whose names are sensitive? If so, decide who needs to be told: product owners, account teams, or, in regulated sectors, compliance staff. Then find the root cause. Was the key pasted into a public repository? Did a build tool bundle it into a front end? Was a log level set too high? Fix that cause, add an automated check so the same mistake is caught earlier next time, and record the incident briefly in a shared place, without including the key.
Do not delete the exposed key before the replacement is deployed, unless you are certain the exposure is being actively abused and a short outage is the lesser evil. Deleting first can turn a disclosure incident into an availability incident, and both are costly. In most cases, replace first, then revoke, within minutes.
If a house key goes missing, a sensible person has the new lock fitted and new keys cut for everyone in the household, and only then throws away the old keys. Changing the lock without giving the family new keys would leave them standing in the street. The order of operations is what makes it a repair, not a second problem.
QDetecting Problems: Scanning, Logs and Alerts
The best incident is the one you catch within minutes. A handful of automated checks do most of the catching.
Catch secrets before they leave
Secret scanning tools look for strings that match known key shapes. They can run on a developer’s laptop before a commit, in your continuous integration pipeline before a merge, and across the history of existing repositories. Many code hosting platforms also offer built-in scanning that alerts you when a recognised secret appears. A recognisable prefix on server-side keys helps these tools find them reliably. Turn the scanning on, route its alerts to a person who will act, and treat each alert as real until proven otherwise.
Watch the signals that point to key trouble
| Signal | What it may mean | Action |
|---|---|---|
| Sudden rise in authentication failures | A revoked, mistyped, or rotated key still in use somewhere | Find which service sends them, and fix its configuration |
| Rise in “forbidden” results | A client-side ID used where a server-side key is required | Correct the key type in that deployment |
| Document refresh failures in local mode | Key problem or network problem | Check key validity, then connectivity |
| Requests from unexpected places | A key in use outside your infrastructure | Compare sources with your known ranges; rotate if unexplained |
| Secret scanner alert | A key committed, logged, or posted somewhere | Treat as exposure; follow the runbook |
| Unexpected flag changes in the audit log | A compromised login or administrative token | Revoke those credentials; review all recent changes |
Logs that help and logs that hurt
Your own services should log that authentication failed, which key name or consumer was involved, and when. They must never log the key itself, nor print configuration objects that contain it. A surprising number of leaks come from debug output that dumps all settings during a failure, exactly when engineers are most likely to paste logs into a ticket for help. Mask secrets in configuration printouts, and configure your logging tools to redact values that match your key shape.
Because you name keys by consumer, you can attach the key name to your metrics. A graph of “authentication failures by key name” shows within seconds which service is struggling after a rotation, instead of leaving you to guess from a pile of identical errors.
A sample key inventory
An inventory need not be fancy. A simple table in a shared, access-controlled document is enough, as long as it is kept current and never contains the key values. The columns that matter are the key’s name, the environment, the consumer, the owner, the storage location, the creation date, and the date of the last rotation.
| Key name | Environment | Consumer | Owner | Stored in | Last rotated |
|---|---|---|---|---|---|
| checkout-api-prod-2026a | Production | Checkout service | Payments team | Secrets manager, production account | Spring |
| catalogue-api-prod-2026a | Production | Catalogue service | Search team | Secrets manager, production account | Spring |
| reports-job-stg-2026a | Staging | Reporting job | Data team | Secrets manager, staging account | Winter |
| edge-proxy-prod-2026a | Production | Edge Proxy | Platform team | Secrets manager, production account | Spring |
Reviewing this table each quarter takes minutes and answers the questions that matter in an emergency: who owns each key, where it lives, and how old it is. It also makes it obvious when a key has no owner, which is the signal to investigate and probably delete.
REnvironments and Key Hygiene
Separate environments are only separate if their keys stay separate. The discipline is simple, and it pays back every day.
flowchart LR
subgraph Dev["Development environment"]
DK["Dev keys"] --> DS["Developer sandboxes and local runs"]
end
subgraph Stg["Staging environment"]
SK["Staging keys"] --> SS["Staging services and automated tests"]
end
subgraph Prod["Production environment"]
PK["Production keys"] --> PS["Production services only"]
end
Dev -. no key crosses over .- Stg
Stg -. no key crosses over .- Prod
Rules that keep environments apart
Production keys stay in production
No laptop, no test suite, and no staging service should ever hold a production key. If it must read production flags, question why.
Tests use test environments
Automated tests use a dedicated environment, or offline mode, so they cannot change or read live behaviour.
Name everything with the environment
Keys, secrets, and variables carry the environment in their names, so a mix-up is visible at a glance.
Check the environment at startup
Have services log which environment their key points to, so a staging service with a production key is spotted immediately.
Short-lived environments
Teams that create a temporary environment for each feature branch or pull request face a special case. Each temporary environment gets its own keys, and those keys should be created automatically, given a short life, and deleted when the environment is torn down. Orphaned keys from forgotten environments are a quiet source of risk, because nobody remembers they exist. Include key cleanup in the same automation that removes the environment, and review the key list regularly for unfamiliar names.
A key inventory is the cure for forgotten keys. A simple table listing each key’s name, environment, owner, purpose, and creation date lets you spot keys with no owner and keys that have lived for too long. Review it every quarter.
A worked example: onboarding a new service
To see these rules in motion, imagine a team launching a new “notifications” service. In the first week, they create three server-side keys: “notifications-dev-2026a” in the development environment, “notifications-stg-2026a” in staging, and “notifications-prod-2026a” in production. Each is stored in the secrets manager of the matching account, readable only by that service’s role. The developers’ laptops use the development key through a local, ignored configuration file. The test pipeline uses the staging key, injected by the pipeline’s secret store, and masked in logs.
At launch, the production key is read only by the production containers. At startup, the service logs the name of the environment its key points to, and the platform team confirms it says production. Three months later, the team records the first rotation in the inventory. A year later, when the service is retired, all three keys are deleted on the same day as the containers. At no point did anyone email a key, paste it into a document, or share one key between environments, and the process cost only a few extra minutes at each stage.
SEdge Proxy, Self-Hosting and Multi-Region Setups
Bigger and more controlled deployments add new places where keys appear. Each needs the same care.
Self-hosted Flagsmith
If you run Flagsmith yourself, environment keys work exactly as described, but the service they point at is yours. The SDKs are given your own API address in addition to the key. Because you also control the database and the servers, you are responsible for protecting the stored keys, for backing up the data that contains them, and for securing the connection between the SDKs and your installation. Use encrypted connections between clients and the server, restrict network access to the management interface, and handle backups of the database as sensitive material, since a backup contains the same secrets as the live system.
The Edge Proxy
An Edge Proxy is a lightweight service placed inside your network to cache environment data and answer SDK requests close to your applications. To do its work, the proxy itself must be configured with the keys of the environments it serves, typically a server-side key, which it uses to fetch environment data, and the matching client-side ID, which it uses to recognise client requests. The proxy therefore becomes a new holder of secrets, and must be treated as one. Store its configuration in your secrets manager, restrict who can read it, and rotate its keys together with the rest.
| Setup | Who holds the server-side key? | Extra care needed |
|---|---|---|
| Flagsmith Cloud, direct | Each backend service | Secrets manager access per service |
| Flagsmith Cloud via Edge Proxy | The proxy, and optionally the services | Protect the proxy’s configuration; limit who can reach it |
| Self-hosted, direct | Each backend service | Protect the database and its backups too |
| Self-hosted with proxy | The proxy | Both of the above |
| Multi-region deployments | Services in each region | Consistent rotation across regions, region-scoped secret access |
Multiple regions and clusters
When the same service runs in several regions, you can use one server-side key for all of them, or one per region. Separate keys per region make it easy to see where traffic comes from and to revoke one region without touching the others, at the cost of more keys to manage. Whichever you choose, rotate in a rolling order, region by region, and make sure each region’s secrets store is updated before its services restart.
Check the current documentation for how your version of the proxy and your SDKs expect keys to be supplied. Details such as option names and supported plans change over time, and a misconfigured proxy can quietly serve the wrong environment’s data.
TDesign Patterns and Anti-patterns for Keys
A few shapes of use keep you safe. A few other shapes appear again and again in incident reports.
Patterns worth adopting
One key per consumer
Every service and job gets its own named key, so revocation is surgical and tracing is easy.
Secrets by identity
Services read keys from a secrets manager using their own platform identity, not a shared password.
Backend-evaluated sensitive flags
Confidential targeting is evaluated on the server and only results reach the browser.
Overlap rotation
New key before old key, with deletion last, so no service ever goes without.
Key inventory
A simple register of every key, its owner, and its purpose, reviewed regularly.
Automated bundle scan
Every front-end build is scanned for secret prefixes before it can be released.
Anti-patterns to avoid
What it is
Using the server-side key inside a browser bundle or mobile app.
Why it hurts
Every user receives a copy of the key, and with it the ability to download your whole targeting rulebook.
Do this instead
Use the client-side ID in clients, or evaluate on your server and send results.
What it is
One shared key for every service, environment-wide, kept in a team document.
Why it hurts
You cannot tell who uses it, you cannot revoke it without breaking everything, and every reader of the document holds production access.
Do this instead
Create one key per consumer and keep each in the secrets manager.
What it is
Putting secrets, tokens, or private addresses inside flag values that clients can read.
Why it hurts
The client-side ID is public, so anyone can read those values. Remote config is for tuning behaviour, not for storing secrets.
Do this instead
Keep real secrets in your secrets manager and use flags only to choose between behaviours.
What it is
Never rotating, and never practising rotation.
Why it hurts
Old keys spread into forgotten scripts and laptops. When an emergency arrives, the team discovers hidden dependencies under pressure.
Do this instead
Rotate on a schedule, using overlap, and rehearse the emergency path at least once a year.
What it is
Using production keys in tests, staging, or on laptops “just to check something”.
Why it hurts
Production keys then live in many places that have weaker controls, and a mistake in a test can touch production data and analytics.
Do this instead
Give each stage its own environment and keys, and let production keys exist only in production.
A pattern for sensitive targeting: split the project
One powerful design is to separate flags by sensitivity. Put everyday interface experiments in one project, readable by many services and by client SDKs. Put confidential targeting, such as pricing logic or eligibility rules, in a second project, readable only by one or two trusted backend services, and never by clients. Each project has its own environments and keys, so the keys for the sensitive project exist in far fewer places. If a key for the everyday project leaks, little of value is revealed; if a key for the sensitive project is ever at risk, the list of places to check is short and well known.
Security is often about reducing how many places hold a secret and how much that secret reveals. Splitting projects by sensitivity does both at once, with no new technology, only a deliberate way of organising what you already have.
UBest Practices Checklist and Testing Your Setup
A checklist you can run through each quarter, plus a few tests that prove your key handling actually works.
Habits that protect you
- Server-side keys only in protected backends
- Client-side ID only in clients
- One named key per consumer and environment
- Secrets manager with identity-based access
- Secret scanning on commits and pipelines
- Rotation with overlap, practised regularly
- Opaque identifiers and no secrets in flag values
- A written incident runbook
Mistakes that hurt
- Confusing the two key types
- Public prefixes on secret variables
- Keys in repositories, images, or chat
- One shared key everywhere
- Production keys on laptops or in tests
- Logging configuration objects
- Deleting the old key before deploying the new
- Forgotten keys with no owner
Tests that prove it works
Bundle scan test
Build the front end and search the output for the server-side prefix. The test fails the build if anything is found.
Wrong-key test
In a test environment, try to download the environment document with the client-side ID and confirm it is refused. This proves the permission boundary you rely on.
Rotation rehearsal
In staging, perform a full overlap rotation, and confirm no authentication errors appear and the old key is gone at the end.
Revocation test
Delete a throwaway key and confirm the service using it fails in the way you expect, with a clear alert, and recovers when given a new key.
Log redaction test
Trigger an error with debug logging on and confirm that no key value appears in any output.
Turn the checklist into a recurring calendar item with a named owner. Security work that depends on someone remembering usually fails; security work that appears in a calendar and ends in a short written note tends to survive staff changes.
A quarterly review agenda
Set aside thirty to sixty minutes each quarter, with security and platform engineers in the room, and walk through the same short agenda every time. Repetition is the point: a familiar agenda is quick, and gaps stand out.
Review the inventory
Check every key has an owner, a consumer, and a recent rotation date. Delete anything unexplained.
Review access
Check who can read production secrets and who holds administrative credentials. Remove anyone who no longer needs access.
Review scanner results
Look at secret scanning alerts since the last review, and confirm that each one was handled and its key replaced.
Review the audit log
Skim flag and setting changes for anything unexpected, especially from automation credentials.
Plan the next rotation
Decide which keys are due, assign owners, and book the rehearsal if it is overdue.
VReal-World and Industry Examples
The split between a public identifier and a secret key is not unique to Flagsmith. It is one of the most common ideas in modern software, and the failures follow familiar patterns too.
Stripe: publishable and secret keys
The payment company Stripe gives each account a publishable key, meant to be embedded in web pages and apps, and a secret key, meant only for servers. The publishable key can only perform limited actions, such as collecting card details into a safe token. The secret key can do far more, including moving money. This is the same design as Flagsmith’s pair, and the same advice applies: the public one may travel, the secret one may not.
Firebase and mapping services: public keys with guard rails
Google’s Firebase documentation explains that the web configuration values shipped with an app identify a project but do not by themselves grant access; security is enforced by separate rules on the server side. Mapping services similarly issue keys that are embedded in web pages, then restrict where those keys may be used. The pattern repeats: a public identifier travels widely, and protection lives in what that identifier is allowed to do.
Uber, 2016: credentials in a repository
In a widely reported 2016 incident, attackers gained access to a private code repository used by Uber engineers, found cloud access credentials stored there, and used them to reach data on tens of millions of people. The lesson for every secret, including a Flagsmith server-side key, is that code repositories are poor safes. Even a private repository can be reached through a stolen login, so credentials should live in a secrets manager.
Toyota, 2022: a key that waited for years
Toyota reported in 2022 that part of the source code for a customer service had been publicly accessible on a code hosting platform for years, and that it contained an access key which could have exposed data for hundreds of thousands of customers. The key had sat in public view for a long time before anyone noticed. It shows why secret scanning and key inventories matter: a leaked key does not announce itself, and it keeps working until someone deletes it.
A common front-end story
Security researchers regularly publish reports of companies whose websites quietly included private keys in their JavaScript bundles, often because a secret was given a “public” variable prefix. These findings are rarely about malice; they are about an innocent build setting. The defence is the one in this guide: name keys by sensitivity, separate front-end and back-end settings, and scan the built output.
An illustrative setup on AWS
To bring everything together, here is one typical arrangement for a product running on Amazon Web Services. It is an example of how the pieces fit, not a required recipe. The single-page front end is served from a content delivery network and carries only the public client-side ID. The backend runs as containers on Amazon ECS. Each service has its own server-side key, stored as a secret in AWS Secrets Manager, and each task role may read only its own secret. Metrics for authentication failures, labelled by key name, flow to Amazon CloudWatch, with alarms. A scheduled rotation job creates a new key in Flagsmith, stores it as a new secret version, triggers a rolling restart, and deletes the old key after verification.
flowchart LR U["Users"] --> CDN["Content delivery network with the front end"] CDN -- "client-side ID only" --> FS["Flagsmith"] U --> ALB["Application Load Balancer"] ALB --> T1["ECS task: checkout service"] ALB --> T2["ECS task: catalogue service"] SM["AWS Secrets Manager"] --> T1 SM --> T2 T1 -- "its own server-side key" --> FS T2 -- "its own server-side key" --> FS T1 --> CW["CloudWatch metrics and alarms"] T2 --> CW Rot["Rotation job"] --> SM Rot --> FS
In this layout, the rotation job needs an administrative credential, which is a different and more powerful secret than the environment keys. Give that credential its own narrowly scoped role, store it separately, and monitor its use closely.
What the incidents have in common
Looking across the examples above, a few patterns repeat. The secret was in the wrong place: a repository, a public bundle, a log. Nobody noticed for a long time, because a leaked key does not announce itself. The damage came from reach: the key could do more than its holder needed. And the fix, once discovered, was always the same: replace the key and find the cause.
Each pattern has a matching defence in this guide. For the wrong place, use a secrets manager and scan builds. For the long silence, use scanners, alerts, and a quarterly review. For excessive reach, create narrow keys per consumer in one environment, and remember that Flagsmith’s own design limits what a server-side key can do. None of these defences is exotic; they are routine habits, and the incidents are reminders of what happens when the habits are skipped.
WFrequently Asked Questions
Short answers to the questions that come up most often about environment keys.
XSummary and Key Takeaways
The whole story in one place: what to remember when you close this page and open your configuration.
Every Flagsmith environment offers two keys because applications run in two kinds of places. The client-side ID is a public address that lets browsers and apps ask what one identity should see. The server-side key is a secret credential that lets trusted backends read flags and download the whole environment document. Keeping the first public and the second private, storing secrets properly, rotating with overlap, and watching for trouble are the habits that keep feature flags a convenience and not a liability.
Key Takeaways
- Two keys, two trust levels. The client-side environment ID is public and limited; the server-side environment key is secret and can download the full rulebook for local evaluation.
- Ask who can read the code. If a stranger can open it, such as a browser, phone app, or shipped program, use only the client-side ID. If it runs privately in your infrastructure, use a server-side key.
- Public is not harmless. Never put secrets in flag values a client can read, use opaque identifiers, hide disabled flags, and evaluate confidential targeting on your server.
- A leaked server-side key discloses, but does not control. It exposes your rules and values without allowing flag changes, yet it must be replaced promptly, because it works until someone deletes it.
- Keep management separate. Environment keys are for consuming flags; dashboard logins and administrative credentials manage them, each with its own lifecycle and storage.
- Store, name and rotate deliberately. Use one named key per consumer and environment, keep it in a secrets manager, and rotate with overlap: new key first, old key last.
- Prepare for the bad day. Turn on secret scanning, scan front-end bundles, watch authentication errors by key name, and keep a rehearsed runbook that replaces first and revokes second.