Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions contents/docs/experiments/exposures.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ showTitle: true

import { CalloutBox } from 'components/Docs/CalloutBox'
import { ProductScreenshot } from 'components/ProductScreenshot'
import ReturningUsersExposures from "./snippets/returning-users-exposures.mdx"

Exposures are the foundation of experiment analysis in PostHog. A user must be **exposed** to your experiment before they can be included in any metric calculations. Understanding how exposures work is crucial for running successful experiments.

Expand Down Expand Up @@ -52,6 +53,10 @@ The exposures chart shows:
| **Distribution percentage** | How users are split across variants |
| **Exposure criteria** | The event being used to determine exposure |

## Ensuring exposures for returning users

<ReturningUsersExposures />

## Custom exposure events

While the default `$feature_flag_called` event works for most experiments, you might need a custom exposure event when:
Expand Down
14 changes: 14 additions & 0 deletions contents/docs/experiments/snippets/returning-users-exposures.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
The `$feature_flag_called` event is deduplicated by the SDK to avoid sending redundant data. In the **web SDK**, this deduplication is per identity by default. Once emitted for a given flag and value, the event is cached across sessions and won't fire again unless `identify` or `reset` is called. If a user checked the feature flag **before** your experiment launched, the event was already cached and won't be re-emitted when the experiment starts. As a result, returning users may not appear as exposed.

> **Note:** Mobile SDKs (iOS, Android, React Native, Flutter) deduplicate per session by default, so this issue does not apply to them.

To fix this in the web SDK, enable `advanced_feature_flags_dedup_per_session` in your [PostHog config](/docs/libraries/js/config#advanced-configuration):

```js
posthog.init('<ph_project_api_key>', {
api_host: '<ph_client_api_host>',
advanced_feature_flags_dedup_per_session: true,
})
```

When enabled, the deduplication cache resets with each new session, so `$feature_flag_called` is re-emitted for every flag checked in that session. This is especially useful when you launch an experiment on a feature flag that was already being evaluated.
5 changes: 5 additions & 0 deletions contents/docs/experiments/troubleshooting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const AssignAnOverrideLight = "https://res.cloudinary.com/dmukukwp6/video
export const AssignAnOverrideDark = "https://res.cloudinary.com/dmukukwp6/video/upload/posthog.com/contents/images/features/experiments/assigning-an-override-dark-mode.mp4"

import { ProductScreenshot } from 'components/ProductScreenshot'
import ReturningUsersExposures from "./snippets/returning-users-exposures.mdx"
export const ReuseFeatureFlagLight = "https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/features/experiments/reuse-feature-flag-for-experiment-light-mode.png"
export const ReuseFeatureFlagDark = "https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/features/experiments/reuse-feature-flag-for-experiment-dark-mode.png"

Expand Down Expand Up @@ -64,6 +65,10 @@ import FAQFalseOrNoneEvents from "../feature-flags/snippets/faq-false-or-none-ev

<FAQFalseOrNoneEvents />

## Why are returning users not showing as exposed to my experiment?

<ReturningUsersExposures />

## Why are my A/B test event numbers lower than when I create an insight directly?

Experiment results only count events that include the experiment's feature flag data. Sometimes, when you capture experiment events, the flags are not loaded yet. This means users don't see the experiment, their events won't have the flag data, and they are not included in the results calculation.
Expand Down
1 change: 1 addition & 0 deletions contents/docs/libraries/js/config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ These are features for advanced users and may lead to unintended side effects if
| `advanced_disable_feature_flags`<br/><br/>**Type:** Boolean<br/>**Default:** `false` | Will keep `/flags` running, but without any feature flag requests. Important: do not use this argument if using surveys, as display conditions rely on feature flags internally. |
| `advanced_disable_feature_flags_on_first_load`<br/><br/>**Type:** Boolean<br/>**Default:** `false` | Stops from firing feature flag requests on first page load. Only requests feature flags when user identity or properties are updated, or you manually request for flags to be loaded. |
| `advanced_only_evaluate_survey_feature_flags`<br/><br/>**Type:** Boolean<br/>**Default:** `false` | Determines whether PostHog should only evaluate feature flags for surveys. Useful for when you want to use this library to evaluate feature flags for surveys only but you have additional feature flags that you evaluate on the server side.
| `advanced_feature_flags_dedup_per_session`<br/><br/>**Type:** Boolean<br/>**Default:** `false` | Scopes `$feature_flag_called` deduplication to the current session instead of per identity. Useful for [experiments](/docs/experiments) where a flag was evaluated before the experiment launched. Without this, returning users won't generate new [exposures](/docs/experiments/exposures). Mobile SDKs already deduplicate per session by default. |
| `feature_flag_request_timeout_ms`<br/><br/>**Type:** Integer<br/>**Default:** `3000` | Sets timeout for fetching feature flags |
| `feature_flag_cache_ttl_ms`<br/><br/>**Type:** Integer<br/>**Default:** `0` (disabled) | Sets a time-to-live for cached feature flag values. When enabled, `getFeatureFlag()` returns `undefined` instead of stale cached values if the cache is older than the TTL, and stale `$feature/` properties are not attached to events. This is useful when `/flags` requests fail (e.g., due to ad blockers or network issues) and you want to avoid using outdated flag values indefinitely. By default (`0` or `undefined`), cached values never expire. |
| `remote_config_refresh_interval_ms`<br/><br/>**Type:** Integer<br/>**Default:** `300000` (5 minutes) | Controls how often feature flags and remote config are automatically refreshed in long-running sessions. Shorter intervals mean fresher flags but more network requests. Longer intervals reduce traffic but flag changes propagate slower. Set to `0` to disable periodic refresh entirely — flags will only update on page load or when you manually call `posthog.reloadFeatureFlags()`. |
Expand Down
Loading