Repository navigation
Description
Bug Description
The public endpoint /array/<project_token>/config decides whether to deliver the mobile Session Replay configuration using the User-Agent, but the cacheable response doesn't include User-Agent in Vary.
As a result, a non-mobile request can store sessionRecording: false in the shared bucket. A native SDK that immediately queries the same URL receives that value as a cache HIT, even if the project has mobile Replay enabled.
This was reproduced on PostHog Cloud US on 2026-07-29. No tokens or project identifiers are included.
How to reproduce
Use a Project API Key from a project with Session Replay enabled and allowed for mobile SDKs. The random query string avoids affecting the canonical bucket consumed by real users:
BASE="https://us-assets.i.posthog.com/array/<project_token>/config"
PROBE="<random-value>"
# 1. Client not recognized as mobile
curl -sS -D headers-non-mobile.txt \
"$BASE?cache_probe=$PROBE" \
-o body-non-mobile.json
# 2. Same exact URL, now with a React Native User-Agent
curl -sS -D headers-mobile-hit.txt \
-H "User-Agent: posthog-react-native/4.61.1" \
"$BASE?cache_probe=$PROBE" \
-o body-mobile-hit.json
# 3. New bucket requested first by the mobile client
curl -sS -D headers-mobile-miss.txt \
-H "User-Agent: posthog-react-native/4.61.1" \
"$BASE?cache_probe=<another-random-value>" \
-o body-mobile-miss.jsonObserved result:
- The non-mobile request returns sessionRecording: false.
- The mobile request to the same URL returns CF-Cache-Status: HIT and also sessionRecording: false.
- The mobile request to a new bucket returns the full Replay configuration.
- The response uses Cache-Control: public, max-age=300.
- Vary includes signals like Origin/Referer and CORS headers, but not User-Agent.
Expected result
Two requests that can receive different responses based on their authorization/classification signal should not share the same cache entry. The cache should vary by the relevant signal, or the decision to enable Replay should not depend on a header excluded from the cache key.
Impact
Native iOS/Android/React Native SDKs query remote config without an Origin and are identified as mobile via User-Agent. A bucket previously populated by another client can disable Session Replay for the duration of the TTL, even when:
- session_recording_opt_in is enabled;
- the project has valid mobile triggers;
- the SDK and native plugin are correctly installed.
The same remote response also participates in native capture activation, which makes the behavior hard to diagnose from the app.
Additional context
The backend logic recognizes mobile SDKs by User-Agent in:
A local mitigation based on removing domain/origin restrictions would widen the capture scope and doesn't seem like a safe alternative. The fix should preserve the current restrictions.
Debug info
- [x] PostHog Cloud US
- [x] Reproduced with posthog-react-native/4.61.1
- [x] Endpoint: /array/<project_token>/config
- [x] Project token and identifiers deliberately omittedMetadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsTodo
Activity
[-][Bug] Remote config móvil puede devolver sessionRecording=false por una caché que no varía por User-Agent[/-][+][Bug] Mobile remote config can return sessionRecording=false due to a cache that doesn't vary by User-Agent[/+]