Once a site grows into a family of subdomains (marketing site, docs, events portal, blog), PostHog setup raises three questions: does every subdomain need the snippet, how do you keep one user as one person across them, and when (if ever) should events go to more than one PostHog project. A fourth question shows up in production: what the client_ingestion_warning event means and whether it is losing data. This guide answers all four.
Every frontend must load PostHog
PostHog only captures events from pages where its JavaScript snippet or SDK actually runs:
- if the main domain loads PostHog, it captures activity there
- if a subdomain doesn’t load PostHog, that subdomain sends no pageviews, autocapture events or custom events
- if several subdomains should be analyzed, each frontend must initialize PostHog
This isn’t specific to PostHog; it’s how client-side analytics works.
If the main domain and a subdomain are deployed independently, both need PostHog initialization. If they share a base template or layout, initializing once in that shared layer may be enough. But that is an architectural shortcut, not an analytics feature: every page that should be tracked must load the client.
Use one project for related subdomains
In most cases, use one PostHog project for every site that is part of the same user journey: main website, help center, events platform, member portal. One project lets you answer cross-site questions:
- how many users move from the marketing site to the product area
- which acquisition source leads to engagement on other subdomains
- where users drop off across the whole web experience
Splitting each subdomain into its own project makes these questions harder. Only do it if there’s a strong governance or data-isolation reason (see when multiple projects make sense).
Example: geeklogbook.com and blog.geeklogbook.com
This site runs exactly this setup. geeklogbook.com and blog.geeklogbook.com are separate deployments, and both initialize PostHog with the same project key. The blog (an Astro site) also tags its events with a site property and sends pageviews itself, because client-side page transitions don’t trigger full page loads:
posthog.init('YOUR_PROJECT_API_KEY', {
api_host: 'https://us.i.posthog.com',
defaults: '2026-01-30',
person_profiles: 'identified_only',
capture_pageview: false,
loaded: function(posthog) {
posthog.register({ site: 'blog' });
}
});
document.addEventListener('astro:page-load', function() {
posthog.capture('$pageview');
});
Neither site sets cross_subdomain_cookie explicitly. Setting it (next section) removes any dependence on the SDK default.
Keep one identity across subdomains
A user who starts on one subdomain and moves to another can show up as two people if the identity cookie is scoped to each subdomain. In posthog-js the relevant setting is cross_subdomain_cookie:
<script>
posthog.init('YOUR_PROJECT_API_KEY', {
api_host: 'https://us.i.posthog.com',
cross_subdomain_cookie: true,
persistence: 'localStorage+cookie'
})
</script>
With this, PostHog stores identification data at the parent-domain level, which keeps the same identity across related subdomains. In many setups that is enough.
Tell subdomains apart in reports
In one shared project you still need to filter by site. The simplest way is to register the hostname as a super property, sent with every event:
posthog.register({
hostname: window.location.hostname
})
Dashboards, funnels and event streams can then filter on values like www.example.com, events.example.com or docs.example.com.
A business-level property is more readable when hostnames are technical, or when several domains map to one category:
posthog.register({ site_section: 'events' })
When journeys still break
Even with shared cookies, cross-site journeys can look wrong:
- self-referrals appear
- a user seems to restart a session when moving between subdomains
- the same person gets multiple identities
The usual causes are cookie policies, browser behavior, inconsistent initialization, or complex navigation between sites. Check:
- the same PostHog project key is used everywhere
cross_subdomain_cookieis enabled- the analytics code loads consistently on every relevant site
- identity calls such as
identify()are implemented the same way in every application
Validate by navigating from one subdomain to another and watching the live events view.
When multiple projects make sense
Some architectures do need one website to send events to several PostHog projects:
- environment separation (development, staging, production)
- multi-tenant platforms, or analytics for several clients from one platform
- data ownership split between teams or companies
- migrating between PostHog projects
- operational data in one project, product analytics in another
Don’t use multiple projects just to get more properties or attributes. One project with richer event properties is the right design for that.
Named instances
posthog-js supports several SDK instances on one page. The third argument of posthog.init names the instance, and each instance sends to its own project with its own API key:
// Default instance: project A
posthog.init('PROJECT_A_API_KEY', {
api_host: 'https://us.i.posthog.com',
autocapture: false,
capture_pageview: false
});
// Named instance: project B
posthog.init('PROJECT_B_API_KEY', {
api_host: 'https://us.i.posthog.com',
autocapture: false,
capture_pageview: false
}, 'project_b');
posthog.capture('signup_completed'); // goes to project A only
posthog.project_b.capture('signup_completed'); // goes to project B only
Events are not copied between instances. Each call goes only to the instance you call it on, so you must send an event to each instance explicitly.
Rules for multiple instances
- Disable automatic capture. With autocapture and automatic pageviews on, every instance sends its own pageviews and clicks, which duplicates or skews data across projects. Send only the events you define.
- Use the same distinct ID. To recognize the same user in both projects, identify them with the same ID on every instance (
posthog.identify(userId)andposthog.project_b.identify(userId)). Otherwise the same person appears as different users in each project. - Duplicate critical events on the backend. For business-critical events (payments, signups, conversions), send the event once from the frontend to your backend, and have the backend send it to project A and project B through PostHog’s capture API. That gives you retries, validation, logging, idempotency and better governance than two browser calls.
client_ingestion_warning: the client-side rate limit
With the default posthog-js configuration you may see this in the browser console:
posthog-js client rate limited.
Config is set to 10 events per second and 100 events burst limit.
and this event in the PostHog UI:
client_ingestion_warning
It means the SDK’s client-side rate limiter fired. The SDK limits how fast a browser can send events, by default:
- events_per_second: 10
- events_burst_limit: 100
Sending more than 10 events per second, or more than 100 in a burst, triggers it (JS SDK configuration).
Are you losing data?
Possibly. When the limiter is active:
- Some events may be delayed.
- Some events may be dropped.
- If the page unloads before the queue flushes, events may be lost for good.
If the warnings come from a single distinctId, the issue is probably one user session or an automated interaction, not a systemic failure.
Why it happens
Normal user interaction rarely exceeds 10 events per second. Typical causes:
- events fired inside rendering loops (for example, a React
useEffectwith missing dependencies) - autocapture firing on constant DOM updates
- scroll or mouse-movement tracking
- repeated retries or programmatic event floods
- session recording combined with very busy UI
Find the cause
Turn on debug mode in the browser console:
posthog.debug(true)
Then use the app and watch event frequency, repeated patterns and capture loops. In PostHog, inspect the event timeline for the affected distinctId.
Raising the limit
You can override the defaults:
posthog.init('YOUR_PROJECT_KEY', {
api_host: 'https://us.i.posthog.com',
rate_limiting: {
events_per_second: 20,
events_burst_limit: 200
}
})
Don’t start here. If the warning comes from a logic error, a higher threshold only hides it. Find the excessive event generation, fix the frontend code, and only then consider changing the limits.
Note that PostHog’s ingestion warnings documentation doesn’t list client_ingestion_warning by name; the behavior is documented under SDK configuration. A GitHub issue references this exact warning: PostHog/posthog#22213.
Gotchas
- A subdomain without the snippet is invisible. Instrumenting the main domain doesn’t cover subdomains with their own pages or frontend runtime.
- Different project keys split one user into two. Every site in the journey must use the same key.
- Named instances don’t fan out.
posthog.capture()only reaches the default instance. - Autocapture plus multiple instances duplicates data. Turn it off on every instance when sending to several projects.
- SPA navigation doesn’t fire pageviews on its own when automatic pageviews are off. Capture
$pageviewon your router’s navigation event, as the blog does withastro:page-load. client_ingestion_warningis a symptom. Treat it as a reason to audit event volume, not something to suppress.
Related
- How PostHog uses ClickHouse for high-performance product analytics explains what happens to these events after ingestion.
- Designing event schemas for product analytics on ClickHouse covers naming events and properties such as
siteorsite_section. - Retries in Python: from a basic retry loop to exponential backoff is the server-side counterpart for the backend-duplication pattern above.