Sazabi
Log sourcesSend to an endpoint

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-js browser 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 connectionless

The 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.

Further reading