Skip to content

Instantly share code, notes, and snippets.

@ranic
Last active October 16, 2024 19:47
Show Gist options
  • Save ranic/80459104def4e4bcd73d5c77b817ee43 to your computer and use it in GitHub Desktop.
Save ranic/80459104def4e4bcd73d5c77b817ee43 to your computer and use it in GitHub Desktop.
Example of setting up tracking via a proxy.
<html>
<head>
<title>Mixpanel Tracking Proxy Demo</title>
<script type="text/javascript">
/**
* Configuration Variables - CHANGE THESE!
*/
const MIXPANEL_PROJECT_TOKEN = YOUR_MIXPANEL_PROJECT_TOKEN; // e.g. "67e8bfdec29d84ab2d36ae18c57b8535"
const MIXPANEL_PROXY_DOMAIN = YOUR_PROXY_DOMAIN; // e.g. "https://proxy-eoca2pin3q-uc.a.run.app"
/**
* Set the MIXPANEL_CUSTOM_LIB_URL - No need to change this
*/
const MIXPANEL_CUSTOM_LIB_URL = MIXPANEL_PROXY_DOMAIN + "/lib.min.js";
/**
* Load the Mixpanel JS library asyncronously via the js snippet
*/
(function(f,b){if(!b.__SV){var e,g,i,h;window.mixpanel=b;b._i=[];b.init=function(e,f,c){function g(a,d){var b=d.split(".");2==b.length&&(a=a[b[0]],d=b[1]);a[d]=function(){a.push([d].concat(Array.prototype.slice.call(arguments,0)))}}var a=b;"undefined"!==typeof c?a=b[c]=[]:c="mixpanel";a.people=a.people||[];a.toString=function(a){var d="mixpanel";"mixpanel"!==c&&(d+="."+c);a||(d+=" (stub)");return d};a.people.toString=function(){return a.toString(1)+".people (stub)"};i="disable time_event track track_pageview track_links track_forms track_with_groups add_group set_group remove_group register register_once alias unregister identify name_tag set_config reset opt_in_tracking opt_out_tracking has_opted_in_tracking has_opted_out_tracking clear_opt_in_out_tracking start_batch_senders people.set people.set_once people.unset people.increment people.append people.union people.track_charge people.clear_charges people.delete_user people.remove".split(" ");
for(h=0;h<i.length;h++)g(a,i[h]);var j="set set_once union unset remove delete".split(" ");a.get_group=function(){function b(c){d[c]=function(){call2_args=arguments;call2=[c].concat(Array.prototype.slice.call(call2_args,0));a.push([e,call2])}}for(var d={},e=["get_group"].concat(Array.prototype.slice.call(arguments,0)),c=0;c<j.length;c++)b(j[c]);return d};b._i.push([e,f,c])};b.__SV=1.2;e=f.createElement("script");e.type="text/javascript";e.async=!0;e.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?
MIXPANEL_CUSTOM_LIB_URL:"file:"===f.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";g=f.getElementsByTagName("script")[0];g.parentNode.insertBefore(e,g)}})(document,window.mixpanel||[]);
/**
* Initialize a Mixpanel instance using your project token and proxy domain
*/
mixpanel.init(MIXPANEL_PROJECT_TOKEN, {debug: true, api_host: MIXPANEL_PROXY_DOMAIN});
/**
* Track an event when the page is loaded
*/
mixpanel.track("[Proxy Demo] Page loaded");
</script>
</head>
<body>
<button onclick="mixpanel.track('[Proxy Demo] Button clicked')">Track event</button>
</body>
</html>
@horcruxxxx
Copy link

horcruxxxx commented Jan 23, 2024

hello , I used the exact given nginx.conf file, I only changed to a specific port because I am using localhost for running my app also on some other port.
but still got this error.

mixpanel.service.ts:14 Mixpanel error: Bad HTTP status: 0 
mixpanel.service.ts:14 
        POST https://localhost:4999/lib.min.js/track/?verbose=1&ip=1&_=1706010860416 net::ERR_SSL_PROTOCOL_ERROR

here is my nginx.conf file code:

events {}
http {
    server {
        listen 4999 default backlog=16384;
        listen [::]:4999 default backlog=16384;

        location /lib.min.js {
            proxy_set_header X-Real-IP $http_x_forwarded_for;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Host $server_name;
            proxy_pass https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js;
        }

        location /lib.js {
            proxy_set_header X-Real-IP $http_x_forwarded_for;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Host $server_name;
            proxy_pass https://cdn.mxpnl.com/libs/mixpanel-2-latest.js;
        }

        location /decide {
            proxy_set_header Host decide.mixpanel.com;
            proxy_set_header X-Real-IP $http_x_forwarded_for;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Host $server_name;
            proxy_pass https://decide.mixpanel.com/decide;
        }

        location / {
            proxy_set_header Host api.mixpanel.com;
            proxy_set_header X-Real-IP $http_x_forwarded_for;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Host $server_name;
            proxy_pass https://api.mixpanel.com/;
        }
    }
}

and thats my Angular project code

myid = "ae02abe7863092fe2xxxxxxxxxxxx"
 MIXPANEL_CUSTOM_LIB_URL = "https://localhost:4999/lib.min.js";
 constructor() {}
 init() {
   mixpanel.init(this.myid, {
     api_host: this.MIXPANEL_CUSTOM_LIB_URL,
     debug: true,
     track_pageview: true,
   });
 }

@ianlet @mgara @jalves @fariazz @StephenHaney @ranic

@arthabus
Copy link

arthabus commented Oct 7, 2024

Does anyone have a working example of express/nodejs proxy setup?

No matter what I try, geocoding doesn't go through even though X-Real-IP is set (tried first value before comma of x_forwarded_for as suggested by @StephenHaney above and tried other variations too) plus other headers as per example are set. The proxy logs show all values are set and sent to mixpanel but still no luck

@ianlet
Copy link

ianlet commented Oct 7, 2024

@arthabus Does anyone have a working example of express/nodejs proxy setup?

No matter what I try, geocoding doesn't go through even though X-Real-IP is set (tried first value before comma of x_forwarded_for as suggested by @StephenHaney above and tried other variations too) plus other headers as per example are set. The proxy logs show all values are set and sent to mixpanel but still no luck

The proxy we set up is in Deno, but you can probably work your way around it with express/nodejs as it's fairly similar:

import { serve } from "https://deno.land/std@0.192.0/http/server.ts";

const port = Number(Deno.env.get("PORT") ?? 3009);

serve(
  async (request: Request, conn: any) => {
    const { pathname } = new URL(request.url);
    if (pathname.startsWith("/instrumentation")) {
      return await proxyMixpanelCall(request, conn);
    }

    // ... 
  },
  { port },
);

async function proxyMixpanelCall(request: Request, conn: any) {
  const headers = new Headers(request.headers);

  const url = new URL(request.url.replace("/instrumentation", "/track"));
  url.port = "";
  url.protocol = "https:";
  url.host = "api.mixpanel.com";

  const remoteAddress = getRemoteAddress(request, conn);
  const hostname = getHostname(request, url);
  headers.set("Host", "api.mixpanel.com");
  headers.set("X-Real-IP", remoteAddress);
  headers.set("X-Forwarded-For", remoteAddress);
  headers.set("X-Forwarded-Host", hostname);

  const proxyRequest = new Request(url.toString(), {
    method: request.method,
    headers,
    body: request.body,
  });

  try {
    return await fetch(proxyRequest);
  } catch (e) {
    console.log("Failed to proxy call to Mixpanel", e);
    return null;
  }
}

function getRemoteAddress(request: Request, conn: any) {
  return request.headers.get("x-forwarded-for") || conn.remoteAddr?.hostname;
}

function getHostname(request: Request, url: URL) {
  return request.headers.get("x-forwarded-host") || url.hostname;
}

@arthabus
Copy link

@ianlet thanks a lot mate! While I was adopting your solution I realised that I didn't proxy the query params so that was the missing bit. After setting the full mixpanel urls properly with query params it all started to work. Maybe this would help someone in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment