Your feature flag service ships a JavaScript SDK to the browser. To decide which features to show, that SDK needs the targeting rules: who gets the new checkout flow, which internal accounts see the admin preview, which customers are on the enterprise plan and which are flagged for churn risk.
Those rules are frequently sent to the client in full, so the client can evaluate them locally without a round trip. Which means anyone who opens the network panel can read your entire targeting configuration, not just the answer for themselves.
This post is what that leaks and how to stop it. For whoever wired up the flag service.
What ships to the browser
Open the request your flag SDK makes on page load. Depending on the provider and how it is configured, the response can include:
Every flag's targeting rules, not just the ones relevant to the current user: which user IDs, email domains, or account IDs are explicitly included or excluded, the percentage rollout and its seed, and the conditions gating each feature.
Flags for features not yet announced. A flag named new-billing-flow-v2 or sunset-legacy-api tells a competitor or a journalist what you are building before you say so, and flag names are rarely written with the assumption that a stranger will read them.
Internal segmentation. A rule targeting plan = enterprise AND mrr > 50000 reveals your pricing tiers and account value bands. A rule excluding accounts flagged churn_risk reveals which customers you have privately classified as likely to leave, which is a strange thing for a customer to discover about themselves.
Kill switches for security controls. If a flag disabling a validation or a rate limit is evaluated client-side, its existence and current state are visible to anyone, which is useful information to an attacker deciding what to try.
Why it happens
Client-side evaluation is faster and works offline, so many flag providers default to shipping the full rule set and evaluating locally, rather than asking the server for one answer per user. The trade is genuinely attractive for latency and it silently converts your targeting configuration into public information.
Server-side evaluation, where the client asks the server for a single response and the server does the rule evaluation, avoids this entirely, because only the answer for that specific user leaves the server. It costs a round trip and it is the right default for anything you would not want a customer to see about themselves or others.
The split worth making
Not every flag needs the same treatment.
Cosmetic and low-stakes flags: colour, copy, layout. Client-side evaluation is fine, and the rules being visible costs nothing.
Flags revealing business information: pricing tiers, account segmentation, churn scoring, anything that would be an odd thing for a customer to learn about themselves or someone else. Evaluate server-side.
Flags gating security-relevant behaviour: rate limits, validation, feature kill switches. Evaluate server-side, without exception, because the flag's existence is itself information worth protecting and its state should never be a client-readable value.
Unreleased feature names. Use a code name unrelated to the feature's function for anything evaluated where the flag list might be visible before launch, and treat the eventual product name as something to reveal on your own schedule rather than in a targeting rule months early.
Check what your SDK actually sends
# Load your app, then read the flag service's own network request
curl -s https://sdk.yourflagprovider.com/client/YOUR_KEY \
-H "Authorization: Bearer $CLIENT_KEY" | jq .
# Does the response include rules, or only evaluated results?
# If it includes rules, does it include ALL flags or only ones relevant
# to the current context?
If the response contains conditions, percentages, or explicit user or account lists rather than a set of already-evaluated true/false values, your targeting configuration is public to anyone who loads the page.
Segment by audience if you must ship rules client-side
Some providers support scoping the client-side payload to only the flags relevant to a given context, rather than the entire project's flag set. If you are committed to client-side evaluation for latency reasons, use this rather than the default of shipping everything, and treat any flag outside that scoped set as one that must never be added carelessly to a broad context.
The naming discipline that costs nothing
Regardless of evaluation model, flag names end up in error messages, in logs, in the client bundle as string literals if referenced directly, and in the network payload if the provider ships names alongside keys. A flag called bypass-fraud-check-for-vip describes a control and its existence in a way a flag called flag-4471 does not.
This is a small thing and it is free: name flags by function to your team, in your internal documentation, and give them opaque identifiers in anything that reaches a client.
The concession
Server-side evaluation costs a round trip per flag check, or per batch of checks, which matters for a page rendering many flag-gated elements and matters more for offline or intermittent-connectivity use cases where client-side evaluation was chosen specifically to avoid that dependency.
So the practical position is not to insist on server-side everywhere. It is to classify your flags once, move the ones revealing business logic or security posture to server-side evaluation, and leave the cosmetic ones as they are. That classification is an afternoon, and it is the same discipline as everything else in this space: decide deliberately rather than accept whichever default the tool shipped with.
The implication
A feature flag service is a targeting engine, and targeting rules are a description of how you segment, price, and manage risk on your customers. Shipping that description to every browser that loads your page is rarely a decision anyone made; it is usually the default of whichever SDK was fastest to integrate.
Open your own network panel and read what the flag request actually contains. If it is more than the answer for you, specifically, that is what every one of your users can currently see about everyone else.