PostHog SDK
Configure a reverse proxy to copy posthog-js browser capture traffic to a Sazabi endpoint while continuing to send to PostHog.
About
Forward browser posthog-js capture traffic — analytics events, identify calls, and session replay snapshots — into Sazabi by routing SDK traffic through a reverse proxy you control. The proxy copies browser capture traffic to Sazabi and still sends PostHog's own SDK, config, and feature-flag traffic to PostHog. This is additive — your app keeps sending to PostHog with its existing project token.
This source captures only what posthog-js sends from the browser. It does
not cover server-side PostHog events — use the PostHog CDP webhook source for
those.
Session replay is controlled by your PostHog project settings, not a posthog.init option.
Prerequisites
- A
posthog-jsbrowser application. - A reverse proxy you control (for example a Next.js rewrite, Nginx, or Cloudflare Worker) that can split PostHog SDK routes by path.
- Your Sazabi intake URL (shown above).
Set up in the dashboard
Plan the reverse proxy
Configure posthog-js to use a reverse proxy path as api_host. Send capture paths (/e/, /i/, /s/) to Sazabi, and send PostHog config, flags, and assets to PostHog.
Use your Sazabi intake URL (shown above) as the upstream destination for capture-path proxy rules.
Initialize posthog-js
Point posthog-js at your reverse proxy path:
JavaScript
import posthog from "posthog-js";
posthog.init("<your phc_* project token>", {
api_host: "/ingest",
ui_host: "https://us.posthog.com",
});Next.js
"use client";
import posthog from "posthog-js";
import { PostHogProvider } from "posthog-js/react";
if (typeof window !== "undefined") {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
api_host: "/ingest",
ui_host: "https://us.posthog.com",
});
}
export function Providers({ children }: { children: React.ReactNode }) {
return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
}Configure rewrites
Route capture paths to Sazabi and all other PostHog paths back to PostHog. For Next.js:
On Next.js, set skipTrailingSlashRedirect: true. Otherwise Next.js sends a
308 redirect on the trailing-slash capture paths (/e/, /i/v0/e/, /s/),
which stops them from matching the Sazabi rule.
// next.config.ts
import type { NextConfig } from "next";
const config: NextConfig = {
// posthog-js posts to trailing-slash capture paths (/e/, /i/v0/e/, /s/). Without
// this, Next.js 308-redirects them to the slash-less path, which no longer matches
// the capture rule below and falls through to PostHog.
skipTrailingSlashRedirect: true,
async rewrites() {
return [
{
source: "/ingest/:path((?:e|i|s)/.*)",
destination: "https://your-intake-host/:path*",
},
{
source: "/ingest/:path*",
destination: "https://us.i.posthog.com/:path*",
},
];
},
};
export default config;Replace your-intake-host with the hostname portion of your intake URL (shown above, without https://).
Set up with the CLI
You can also register the PostHog SDK log source with the Sazabi CLI (installed and authenticated — see CLI reference).
Registering the source mints the same intake URL the dashboard shows above (the public key is embedded in its hostname):
sazabi log-sources create posthog_sdk --mode connectionlessThe command prints the intake URL for the new source — copy the whole URL and point your sender at it using the configuration shown above. Run sazabi log-sources get <log-source-id> at any time to reprint the endpoint, or sazabi log-sources list to see every log source in the project.
Verify
Load your application and generate a browser event (for example a page view or a button click). Open the Intake page in the Sazabi dashboard and confirm records from the PostHog SDK source appear. Records typically arrive within a minute or two.
Troubleshooting
No records appear — Confirm the rewrite routes for /e/, /i/, and /s/ paths resolve to your Sazabi intake hostname. Check the browser network tab for the capture request and verify it returns a 2xx from Sazabi.
Session replay snapshots not forwarding — Session replay uses the /s/ path. Confirm your proxy rule covers /s/.* and is not blocked by a more general PostHog passthrough rule that precedes the Sazabi capture rule.
Next.js 308 redirect loop — Without skipTrailingSlashRedirect: true, Next.js redirects trailing-slash paths before the rewrite runs. The redirect destination (/e without the slash) no longer matches the capture rule and falls through to PostHog instead of Sazabi.