nginx 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 nginx as a reverse proxy for PostHog.
How it works
nginx acts as an intermediary between your users and PostHog. When a user triggers an event, the request goes to your nginx server first, which forwards it to PostHog's servers and returns the response.
Here's the request flow:
- User triggers an event in your app
- Request goes to your nginx server (e.g.,
e.yourdomain.com) - nginx forwards the request to PostHog with the correct headers
- PostHog processes the request and returns a response
- nginx returns PostHog's response to the user
This works because ad blockers see requests going to your domain, not PostHog's. The proxy is transparent to the browser.
Why two location blocks? PostHog uses separate domains for API requests and static assets. Your nginx config needs two location blocks:
/static/: Routes tous-assets.i.posthog.comoreu-assets.i.posthog.comfor the JavaScript SDK and other static files/: Routes everything else tous.i.posthog.comoreu.i.posthog.comfor event capture, feature flags, and API calls
Prerequisites
- A server with root or sudo access
- A domain with DNS pointing to your server
- Ports 80 and 443 open for HTTP/HTTPS traffic
Choose your setup option
Both options accomplish the same goal. Choose based on your infrastructure:
- Direct installation: Install nginx on your server and configure it manually. Use this for dedicated servers or VMs.
- Docker: Run nginx in a container with environment variables for region configuration. Use this for containerized infrastructure or local testing.
Option 1: Direct installation
- 1
Install nginx
- 2
Configure nginx
Create a new configuration file at
/etc/nginx/sites-available/posthog-proxy:nginxReplace
e.yourdomain.comwith your subdomain. Replaceuswitheufor EU region.Here's what each directive does:
proxy_pass: The upstream URL nginx forwards requests to. The trailing slash is important—it tells nginx to replace the matched location path.proxy_set_header Host: Sets the Host header sent to PostHog. Without this, PostHog receives your domain and can't route the request, causing 401 errors.proxy_ssl_server_name on: Enables Server Name Indication (SNI) for the upstream SSL connection. Required because PostHog uses virtual hosting.proxy_ssl_name: The hostname to use for SNI. Must match the upstream domain.
Enable the site by creating a symlink:
TerminalSee nginx proxy documentation for more configuration options.
- 3
Test and reload nginx
Test your configuration for syntax errors:
TerminalIf the test passes, reload nginx to apply the configuration:
Terminal - 4
Add SSL/TLS
RecommendedUse Certbot to add free SSL certificates from Let's Encrypt:
TerminalCertbot automatically updates your nginx configuration to redirect HTTP to HTTPS and manages certificate renewal.
- 5
Update your PostHog SDK
In your application code, update your PostHog initialization to use your proxy subdomain:
Replace
e.yourdomain.comwith your actual subdomain. Verify your setup
CheckpointConfirm events are flowing through your proxy:
Test the proxy directly with curl:
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 subdomain (e.g.,
e.yourdomain.com)Verify the response status is
200 OKCheck the PostHog app to confirm events appear
If you see errors, check troubleshooting below.
Option 2: Docker
This option runs nginx in a container with environment variables for the PostHog region.
- 1
Create a Dockerfile
Create a
Dockerfile:dockerfileThe
envsubstcommand replaces the${POSTHOG_CLOUD_REGION}variable in your config with the actual value at runtime. This lets you use the same image for both US and EU regions. - 2
Create the nginx config
Create a file named
nginx.conf:nginxThe
resolverdirective configures DNS resolution for the upstream domains. This is required in containers because nginx resolves hostnames at startup, and container DNS may not be available immediately. - 3
Build and run the container
Build the Docker image:
TerminalRun the container:
TerminalReplace
uswitheufor EU region. - 4
Update your PostHog SDK
In your application code, update your PostHog initialization:
Replace
localhost:8080with your actual host and port in production. Verify your setup
CheckpointConfirm events are flowing through your proxy:
Test the proxy directly:
TerminalOpen your browser's developer tools and go to the Network tab
Trigger an event in your app
Look for requests to your proxy host
Verify the response status is
200 OKCheck the PostHog app to confirm events appear
If you see errors, check troubleshooting below.
Troubleshooting
502 Bad Gateway errors
If nginx returns 502 Bad Gateway, it can't reach PostHog's servers:
- Verify your server can make HTTPS requests to PostHog domains:Terminal
- Check that
proxy_ssl_server_name onis set in your config - For Docker, ensure the
resolverdirective is configured
SSL/TLS handshake errors
If you see SSL errors in nginx logs:
- Verify
proxy_ssl_namematches the upstream domain exactly - Check that your server's CA certificates are up to date
- For Docker, the alpine image may need CA certificates installed:
apk add ca-certificates
CORS errors
If you see Access-Control-Allow-Origin errors in the browser:
- Add CORS headers to your nginx config:nginx
- The
proxy_hide_headerremoves PostHog's CORS header so you can set your own
Configuration file locations
Common nginx config locations by platform:
- Ubuntu/Debian:
/etc/nginx/sites-available/with symlinks in/etc/nginx/sites-enabled/ - CentOS/RHEL:
/etc/nginx/conf.d/ - macOS (Homebrew):
/usr/local/etc/nginx/
Run nginx -t to test your config and see which files nginx is loading.