Node reverse proxy
Contents
- If you use a self-hosted proxy, PostHog can't help troubleshoot. Use our managed reverse proxy if you want support.
- Use domains matching your PostHog region:
us.i.posthog.comfor US,eu.i.posthog.comfor EU. - Don't use obvious path names like
/analytics,/tracking,/telemetry, or/posthog. Blockers will catch them. Use something unique to your app instead.
This guide shows you how to use Node.js as a reverse proxy for PostHog using the built-in fetch API and http module.
How it works
This proxy uses Node.js as a lightweight server that intercepts requests to your proxy path and forwards them to PostHog. It's useful when you're already running a Node.js server and don't want to add a separate proxy tool.
Here's the request flow:
- User triggers an event in your app
- Request goes to your Node.js server (e.g.,
yourdomain.com/ph) - The proxy middleware intercepts requests matching your prefix
- It rewrites headers and forwards the request to PostHog using
fetch - PostHog's response is returned to the user under your domain
The proxy preserves the user's real IP address by setting X-Forwarded-For headers, ensuring accurate geolocation data in PostHog.
When to use this
Use a Node.js proxy when:
- You're already running a Node.js server and want to add proxying without additional infrastructure
- You need custom logic like header modification, logging, or request filtering
- You want a simple setup for development or low-traffic production sites
For high-traffic production sites, consider a dedicated reverse proxy like nginx or Caddy, which handle concurrent connections more efficiently.
Prerequisites
- Node.js 18 or later (for native fetch support)
- An existing Node.js server or willingness to run one
Setup
- 1
Create the proxy module
Create a file named
proxy.js:This module exports middleware that:
- Routes
/static/*requests to PostHog's asset server and everything else to the main API - Sets the
hostheader so PostHog can route the request correctly - Preserves the client's real IP address using
X-Forwarded-ForandX-Real-IPheaders - Removes cookies and connection headers that shouldn't be forwarded
- Handles compressed responses by removing encoding headers
- Routes
- 2
Create the server
Create a file named
server.js:JavaScriptReplace
https://yourdomain.comwith your domain for CORS configuration. - 3
Install dependencies
Install the CORS package:
Terminal - 4
Start the server
Run your server:
Terminal - 5
Update your PostHog SDK
In your application code, update your PostHog initialization:
Replace
localhost:3000with your actual server address in production. Verify your setup
CheckpointConfirm events are flowing through your proxy:
Test the proxy directly:
TerminalYou should see a
200 OKresponse.Open your browser's developer tools and go to the Network tab
Trigger an event in your app
Look for requests to your proxy path
Verify the response status is
200 OKCheck the PostHog app to confirm events appear
If you see errors, check troubleshooting below.
Production considerations
For production deployments:
- Use a process manager: Tools like PM2 keep your server running and restart it on crashes
- Add rate limiting: Prevent abuse by limiting requests per IP using packages like
express-rate-limit - Implement logging: Log proxy errors for debugging with structured logging
- Consider a reverse proxy: Put nginx or Caddy in front of Node.js for better performance with high traffic
- Add health checks: Create a
/healthendpoint for monitoring
Troubleshooting
CORS errors
If you see Access-Control-Allow-Origin errors:
- Verify the
originincors()matches your website's domain exactly - For multiple origins, use an array:
cors({ origin: ['https://yourdomain.com', 'http://localhost:3000'] }) - For development, you can use
cors({ origin: '*' })but don't do this in production
502 Bad Gateway errors
If the proxy returns 502 errors:
- Verify your server can reach PostHog domains:Terminal
- Check that the
API_HOSTandASSET_HOSTvalues match your PostHog region - Ensure no firewall is blocking outbound HTTPS traffic
All users show same location
If geolocation data is wrong or all users appear in the same location:
- Verify the
X-Forwarded-ForandX-Real-IPheaders are being set inproxy.js - If you're behind another proxy (like nginx), ensure it's passing the original client IP
- Check that your hosting platform isn't stripping these headers
Note: This implementation is based on work by SimonSimCity.