Skip to content

PostHog Integration Guide

This document describes how surkyl.com integrates with PostHog using the godseye-proxy service.

surkyl.com uses godseye-proxy as a reverse proxy for PostHog Cloud. This provides:

  • Ad blocker bypass - Uses e.surkyl.com instead of us.i.posthog.com
  • First-party tracking - Better privacy compliance
  • Large payload support - Handles up to 64MB for session recordings
  • Production-grade - Health checks, metrics, and proper error handling
┌─────────────────────────────────────────────────────────────┐
│ Browser (surkyl.com) │
│ ↓ │
│ posthog.init('phc_xxx', { │
│ api_host: 'https://e.surkyl.com', │
│ ui_host: 'https://us.posthog.com' │
│ }) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ DNS: e.surkyl.com → godseye-proxy server │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ godseye-proxy (Port 64980) │
│ - Forwards requests to PostHog │
│ - Overrides Host header │
│ - Handles CORS │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ PostHog Cloud (us.i.posthog.com) │
└─────────────────────────────────────────────────────────────┘

Add these to your .env file (see .env.example):

Terminal window
# Required: Your PostHog project key
PUBLIC_POSTHOG_KEY=phc_your_project_key_here
# Required: godseye-proxy URL
# Production: https://postit.godseye.surkyl.com
# Development: http://localhost:64980 (if running godseye-proxy locally)
PUBLIC_POSTHOG_PROXY_HOST=https://postit.godseye.surkyl.com
# Optional: Environment (defaults to 'development')
PUBLIC_ENV=production
# Optional: Enable PostHog in development (disabled by default)
PUBLIC_POSTHOG_ENABLED=true

Runtime Environment Variables:

With Astro’s SSR mode and the Node adapter, environment variables are read from process.env at runtime. This means:

  • ✅ You can change environment variables in Dokploy/Docker
  • ✅ Changes take effect after restarting the container (no rebuild needed)
  • ✅ Same build can be deployed to different environments with different configs
  • ✅ Environment variables are read fresh on each request

The PostHog client is configured in src/components/posthog.astro:

posthog.init(posthogKey, {
api_host: posthogApiHost, // e.surkyl.com (via env var)
ui_host: 'https://us.posthog.com', // For toolbar/feature flags
person_profiles: 'identified_only',
capture_pageview: true,
capture_pageleave: true,
debug: false,
});
Section titled “Option 1: Run godseye-proxy Locally (Recommended)”

Terminal 1: Start godseye-proxy

Terminal window
cd apps/godseye-proxy
nx serve godseye-proxy
# Runs on http://localhost:64980

Terminal 2: Start surkyl-com

Terminal window
cd apps/surkyl-com
# In your .env file:
# PUBLIC_POSTHOG_PROXY_HOST=http://localhost:64980
# PUBLIC_POSTHOG_ENABLED=true
nx serve surkyl-com

Point your local dev environment to the production godseye-proxy:

.env
PUBLIC_POSTHOG_PROXY_HOST=https://e.surkyl.com
PUBLIC_POSTHOG_ENABLED=true

Don’t set PUBLIC_POSTHOG_ENABLED in your .env file. PostHog will be disabled in development mode.

Navigate to http://localhost:4321 and open the console.

window.posthog;
// Should return PostHog object (not undefined)

Open the Network tab and filter by “localhost:64980” or “e.surkyl.com”:

  • Look for requests to /static/array.js (PostHog script)
  • Look for requests to /decide/ (PostHog config)
  • Look for requests to /e/ or /batch/ (events)

All requests should go through godseye-proxy, NOT directly to us.i.posthog.com.

posthog.capture('test_event', {
test: true,
timestamp: new Date().toISOString(),
});

Check the Network tab for a new request.

  1. Go to https://us.posthog.com
  2. Navigate to Events
  3. Look for $pageview and test_event events
  4. Events should appear within 5-10 seconds
  1. Deploy godseye-proxy to your server

    • See godseye-proxy/README.md for deployment instructions
    • Configuration is at configs/godseye/godseye.config.yml (repo root)
    • Ensure it’s running on port 64980
  2. Configure DNS

    e.surkyl.com → A/CNAME → [godseye-proxy-server-ip]
  3. Setup SSL/TLS

    • Use Cloudflare, Caddy, or nginx to terminate SSL
    • Ensure https://e.surkyl.com is accessible
  • godseye-proxy deployed and running
  • DNS e.surkyl.com pointing to godseye-proxy server
  • SSL/TLS configured (https://e.surkyl.com accessible)
  • godseye-proxy health check responding: https://e.surkyl.com/health
  • Environment variables set in production:
    Terminal window
    PUBLIC_POSTHOG_KEY=phc_your_project_key_here
    PUBLIC_POSTHOG_PROXY_HOST=https://e.surkyl.com
    PUBLIC_ENV=production
  • surkyl-com deployed with new env vars
  • Test with ad blocker enabled (should still work!)
  • Verify events in PostHog dashboard

godseye-proxy exposes a health endpoint:

Terminal window
curl https://e.surkyl.com/health
# {"status":"healthy","service":"godseye-proxy"}

Use this for:

  • Docker healthchecks
  • Kubernetes liveness probes
  • Uptime monitoring

godseye-proxy exposes Prometheus metrics:

Terminal window
curl https://e.surkyl.com/metrics

godseye-proxy logs in JSON format (configurable):

Terminal window
# View logs
docker logs godseye-proxy
# Follow logs
docker logs -f godseye-proxy
  1. Check godseye-proxy logs for errors
  2. Verify DNS points to godseye-proxy: dig e.surkyl.com
  3. Check SSL certificate: curl -I https://e.surkyl.com/health
  4. Verify env var PUBLIC_POSTHOG_PROXY_HOST is set correctly
  5. Test godseye-proxy directly:
    Terminal window
    curl https://e.surkyl.com/health

Update godseye-proxy config (apps/godseye-proxy/configs/godseye.config.yml):

cors:
allow_origins:
- 'https://surkyl.com'
- 'https://www.surkyl.com'
- 'http://localhost:4321' # For development

Restart godseye-proxy after config changes.

The PostHog toolbar authenticates via ui_host. Ensure it’s set correctly:

posthog.init(posthogKey, {
api_host: 'https://e.surkyl.com', // Proxied
ui_host: 'https://us.posthog.com', // Direct (do NOT proxy this)
});

godseye-proxy supports up to 64MB payloads by default. If you need more:

Update godseye-proxy config:

limits:
max_body_size: 134217728 # 128MB

Restart godseye-proxy.

  1. Check subdomain name - Don’t use obvious tracking names like:

    • analytics.surkyl.com
    • tracking.surkyl.com
    • posthog.surkyl.com
    • e.surkyl.com (good!)
  2. Verify requests go through proxy:

    • Open Network tab
    • Requests should go to e.surkyl.com
    • NOT to us.i.posthog.com
  3. Test with different ad blockers:

    • uBlock Origin
    • AdBlock Plus
    • Brave Browser

Previous Setup:

  • Used custom Astro endpoint: src/pages/ingest/[...path].ts
  • Requests went to /ingest/* on same domain
  • Limited to Astro’s default payload size

Current Setup:

  • Uses dedicated godseye-proxy service
  • Requests go to e.surkyl.com (or configured proxy host)
  • Supports up to 64MB payloads
  • Production-grade with health checks and metrics

What Changed:

  1. Removed src/pages/ingest/[...path].ts (custom proxy)
  2. Updated src/components/posthog.astro (use env var for proxy host)
  3. Added PUBLIC_POSTHOG_PROXY_HOST env var
  4. Added godseye-proxy configuration

For issues or questions:

  1. Check godseye-proxy logs
  2. Review this documentation
  3. Check PostHog dashboard for events
  4. Review the migration plan for rollback procedures