PostHog Product Analytics Cookie Consent Integration Guide: Self-Hosted and Cloud Deployment Playbook for 2026
PostHog is the product analytics platform that engineering teams reach for when they want product analytics, session replay, feature flags, experimentation, surveys, and web analytics in a single stack rather than five vendors stitched together. That bundling is the value proposition. It is also why a default PostHog deployment carries more concentrated consent obligations than almost any other tool a publisher will install. The same posthog.init() call that captures product events can start recording sessions, evaluate feature flags against a persistent identifier, and queue survey impressions — and each of those activities engages a different legal basis under GDPR, ePrivacy, and the CCPA. The good news is that PostHog ships with one of the most granular consent APIs in the analytics ecosystem; the work is in actually using it. This guide walks through how to deploy PostHog so that the legal posture matches the technical reality across both self-hosted installations and PostHog Cloud, across the US and EU regions, and across the full surface of analytics, replay, flags, and surveys.
Why PostHog requires consent — and why the answer is not the same for every module
PostHog's web SDK is not a passive page-tag. On a default initialisation the SDK sets one or more first-party persistence entries — by default a combined cookie plus localStorage scheme keyed under ph_{projectKey}_posthog — generates a stable distinct identifier, captures the initial pageview with referrer, UTM parameters, and click identifiers, and opens a long-lived event channel to the configured ingestion host. If session replay is enabled at the project level the SDK additionally begins streaming a continuous record of the rendered DOM, mouse coordinates, and input events. If feature flags are configured the SDK evaluates them against the distinct identifier, persists the decisions for the session, and emits a $feature_flag_called event for each evaluation.
Each of these activities maps to a different consent gate. Persisting a distinct identifier is a storage-and-access operation under Article 5(3) of the ePrivacy Directive and requires prior, freely given, specific, informed, and unambiguous consent across the EEA, the UK, and any jurisdiction that has imported the same standard. Capturing behavioural events is processing of personal data under the GDPR because the combination of identifier, IP address, and behavioural trace is sufficient to single out an individual. Session replay sits in a separate, stricter category under the EDPB's session-replay guidance — replay captures rendered DOM and any unmasked input field and requires explicit, granular consent that is distinct from generic analytics consent. Feature flag evaluation is the subtle one: a flag check that returns a boolean does not itself require consent, but persisting the user's flag assignment to a stable identifier across sessions does, because that is how flag-based experimentation creates traceable user-level data.
What PostHog writes before consent — and what must be suppressed
The default PostHog Browser SDK writes the following on initialisation: one combined cookie and localStorage entry under ph_{projectKey}_posthog containing the distinct identifier, session identifier, and feature-flag decisions, plus, when session replay is enabled, an additional in-memory recording buffer that flushes to the ingestion endpoint every few seconds. The SDK also sends an immediate $pageview event and, if autocapture is on, every subsequent click, form interaction, and rage-click on the page.
The recommended pattern is to initialise PostHog with opt_out_capturing_by_default: true and disable_session_recording: true, then flip both flags once the consent banner returns a positive signal. This is the integration posture that the PostHog documentation now recommends, and it is the posture that survives a regulator's review because it inverts the default: nothing is captured until consent is recorded, and the SDK provides a clean transition rather than a stateful retrofit.
The cookies, localStorage entries, and identifiers PostHog persists
The Browser SDK 1.x writes one persistence entry per project, keyed under ph_{projectKey}_posthog, with a 365-day expiry by default. The persistence init option controls the underlying mechanism: cookie only, localStorage only, localStorage+cookie (the default), sessionStorage, or memory. For a consent-first deployment, memory persistence is the right default when consent has not yet been granted — it ensures no identifier outlives the page session — with a transition to localStorage+cookie once consent flips. The SDK also writes an opt-in or opt-out marker under __ph_opt_in_out_{projectKey} to remember the user's choice across visits; that marker itself is a strictly-necessary cookie because it persists a consent decision.
Mapping PostHog modules to consent frameworks
PostHog does not natively implement IAB TCF or the IAB Global Privacy Platform, and it is not built as an ad-tech vendor. It does, however, integrate with Google Consent Mode v2 through publisher-side bridging, expose a native opt-in and opt-out API, and respect the Do Not Track header via the respect_dnt init option. The pattern that works in production treats each PostHog module as a separate gate bound to a specific CMP signal.
- Product analytics events bind to the analytics purpose. In TCF terms this is most commonly purpose 8 (measure content performance) combined with purpose 1 (store and/or access information). For Consent Mode this maps to analytics_storage.
- Session replay sits behind a stricter gate. PostHog's session replay captures the rendered DOM and any unmasked input field, so a preferences-level or research-level opt-in distinct from analytics is the defensible posture under the EDPB's guidance.
- Feature flags and experimentation can run with ephemeral, in-memory evaluation under a legitimate-interest basis when the goal is only to vary the rendered page. Persistent flag assignment tied to a stable identifier across sessions requires the same consent as analytics, because that is when the flag becomes a traceable user-level data point.
- Surveys and feedback bind to the same gate as session replay when they collect free-text input, or to the analytics gate when they only collect ratings or single-select responses.
The integration pattern that works
The reference deployment has four parts: a CMP that exposes a real-time consent change event, a deferred bootstrap that initialises PostHog with capture and replay disabled, a consent listener that flips opt_in_capturing and the recording switch when the relevant gates open, and a withdrawal path that calls opt_out_capturing, stops the replay buffer, and clears persistent identifiers via the SDK's reset API.
Web implementation
The cleanest pattern is to load the PostHog SDK on every page but call posthog.init(apiKey, { api_host: 'https://eu.posthog.com', opt_out_capturing_by_default: true, disable_session_recording: true, persistence: 'memory', respect_dnt: true }) as the initial bootstrap. Subscribe to the CMP's consent change event. When the analytics category transitions to true, call posthog.set_config({ persistence: 'localStorage+cookie' }) followed by posthog.opt_in_capturing(); this re-enables event capture and the persistence transition starts writing the distinct identifier. When the session-replay category transitions to true, call posthog.startSessionRecording(). When either gate withdraws, call posthog.opt_out_capturing() for the analytics gate or posthog.stopSessionRecording() for the replay gate, expire the relevant persistence entries via posthog.reset(), and dispatch a deletion request through PostHog's GDPR API if the user has invoked their erasure right.
Region selection: PostHog Cloud US vs EU vs self-hosted
PostHog operates two cloud regions — US (us.posthog.com) and EU (eu.posthog.com) — and supports self-hosted installations on the customer's own infrastructure. For EEA and UK traffic, the EU cloud is the right default; it keeps ingestion, processing, and storage inside the EEA and reduces the Schrems II exposure that any US-region analytics deployment carries. The api_host init option pins the region. Self-hosted PostHog moves the entire stack onto the publisher's own infrastructure, which is the strongest data-residency posture but does not remove consent obligations — the legal basis follows the data, not the operator. Whichever option is chosen must be reflected in the privacy notice and the controller's record of processing.
Server-side capture
PostHog supports server-side event ingestion via the Python, Node, Go, Ruby, and PHP SDKs. Server-side events are not exempt from consent — the legal basis follows the data — but server-side ingestion gives the publisher full control over which fields are emitted and when. A common pattern is to capture a minimal essential-only event set server-side under legitimate interest, then enrich those events with behavioural detail from the client only after the user has granted analytics consent. The server-side and client-side events join on the same distinct identifier when consent has flipped; before consent, the server-side events are emitted with an anonymous, ephemeral identifier that is reset on every session.
Validating the integration and the audit trail
The validation step is what regulators check and what publishers most often skip. A correctly integrated PostHog deployment must pass four tests in sequence. First, a clean browser session with the banner shown but no choice made must produce zero requests to the configured api_host beyond the SDK file fetch, zero ph_ cookies in document.cookie, and zero ph_ entries in localStorage. Second, declining analytics must keep that state — no capture, no recording, no persistent identifier. Third, accepting analytics must produce an immediate $opt_in event, the expected ph_{projectKey}_posthog persistence entry with correct SameSite attributes, and event traffic flowing to the configured host. Fourth, withdrawing consent after the fact must immediately stop further capture and replay, expire persistent identifiers, and trigger a deletion request via PostHog's GDPR API if the user has invoked erasure.
The audit-trail expectation under the EDPB's 2023 cookie banner guidelines and the renewed 2026 task force priorities is that the publisher can prove, for any given event in the PostHog project, that the user who generated it had given valid consent at the moment of capture. The standard pattern is to set the consent version and timestamp as person properties on the distinct ID via posthog.setPersonProperties({ consent_version: 'v3', consent_ts: ts }) so that any individual event is traceable back to a specific consent log entry. A correctly gated deployment, paired with region selection that matches the audience, persistence that defaults to memory, and a deletion path that activates on withdrawal, is what turns PostHog from a regulatory concentration risk into a defensible centre of the product analytics stack.