Repository navigation
feat(tracing-headers): allow an optional posthog-js cookie fallback - #116
DanielVisca wants to merge 1 commit into
Conversation
Server middleware may read the session id, and the distinct id of an identified visitor, from the posthog-js cookie when a request has neither tracing header. The option is off by default, honors the consent cookie, and checks session liveness like posthog-js. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: 80f44d9e-704d-4f9c-93d2-bd37d55ab1c2
|
|
||
| ### Requirement: Cookie fallback identity and consent | ||
|
|
||
| With the fallback on, the middleware SHALL read only the configured project's cookie, `ph_<token>_posthog` with `+`, `/`, `=` in the token replaced by `PL`, `SL`, `EQ`. It SHALL ignore the cookie when the consent cookie `__ph_opt_in_out_<token>` is no-like, or absent under configured opt-out by default. It SHALL use the cookie `distinct_id` only when `$user_state` is `identified`. |
There was a problem hiding this comment.
[P2/design] Retain anonymous cookie distinct IDs
Consider retaining the cookie’s distinct_id for anonymous visitors as well, with $process_person_profile: false when that anonymous cookie identity is used.
The cookie already exposes $user_state. Discarding the distinct ID means captures get generated identities, losing browser/backend identity correlation. That is not necessary to prevent profile creation—the event’s $process_person_profile property controls that independently.
This follows Next’s current identity propagation. Preserve existing explicit-capture and authenticated-identity precedence; do not blanket-disable person processing when a different identity overrides the cookie.
An existing profile can still be associated with the event or trigger the backend’s force_upgrade behavior. The intended guarantee is avoiding new profile creation from the anonymous fallback, not guaranteeing every such event remains classified as personless.
Please update the identified-only clause, anonymous-visitor scenario, and corresponding archived artifacts.
Acceptance sketch—not executed: Enable fallback, grant consent, omit tracing headers, and provide a live cookie with distinct_id: anon-123 and $user_state: anonymous. Capture without explicit identity. Expect distinct_id: anon-123, the cookie session ID, and $process_person_profile: false. The current scenario instead requires a generated distinct ID.
There was a problem hiding this comment.
chatted with my agent regarding the above. overall, this spec is essentially how @posthog/next is currently working (with some small deviations; though, i'd be open to aligning against the details here). excited to see that you've landed on a similar approach here and it'd be great to get this formalized.
curious what your take on this is
Problem
Server SDKs link backend events to a browser session only through the
X-POSTHOG-*tracing headers, and most apps never settracing_headers. posthog-js already keeps the distinct ID, the session ID and the user state in its first-party cookie, and the browser sends that cookie on every same-site request. posthog-python and posthog-node now add an off-by-default option to read it, but the spec lists only headers as server input.Changes
This adds three requirements to
tracing-headers, with scenarios:PL/SL/EQtoken replacement), honors the consent cookie and opt-out by default, and uses the distinct ID only for identified visitors.The change is synced into
openspec/specs/tracing-headers/spec.mdand archived underopenspec/changes/archive/2026-10-09-add-optional-posthog-js-cookie-fallback/.Implementations:
How did you test this?
openspec validate tracing-headers --type spec --strictpasses.openspec validate --specs --strict(CLI 1.14.1) reports the same failures in other specs with and without this change.Created with PostHog from a Slack thread
🤖 Generated with Claude Code