Serve Insights Orbit from your own domain
Pass the script and events through your own server so fewer ad blockers stop them, and use a proxy token so each visitor's address still counts for places and unique visitors.
How-to guide6 min read
Some ad blockers block analytics scripts on other domains, such as api.bifrostorbit.com. A first-party proxy serves the script and takes events on your own domain, for example under /stats/, and your server passes them on to Insights Orbit. Visitors' browsers then only talk to your website.
A proxy needs a proxy token to count correctly. This guide sets up both.
Why the token matters
Insights Orbit uses the address a request comes from, for a moment, to recognise a visitor for the day and to find their country and region. Through a proxy, every request comes from your server. Without a token:
- every visit shows your server's country and region
- people who use the same browser on the same day count as one visitor, so visitors are undercounted
- if your server runs in a data centre, as most do, every visit is left out as data-centre traffic, unless an Admin switches Leave out data centres and hosting networks off under Site settings
- all your visitors share one rate limit of 120 requests a minute, so a busy website gets
429answers and loses events
Pages, sources and events are still counted. With the token, your server passes on each visitor's address, and Insights Orbit counts as if the browser had sent the request itself. The address is used in memory and never stored, as without a proxy.
Anyone can set a header, so Insights Orbit trusts the address your server passes on only when the request also carries the site's proxy token.
1Create the proxy token
- An Admin opens the site in Insights Orbit, then Site settings.
- Under First-party proxy, select Create a token.
- Copy the line it shows, such as
INSIGHTS_PROXY_TOKEN=ins_proxy_…. It is shown only once; Insights Orbit keeps only a scrambled form.
Each site has one token. Replace the token makes a new one and stops the old one at once. Remove the token stops it too. Creating and removing tokens is in the audit trail.
2Keep it on your server
Add the token to your website's server settings, such as .env.local and your hosting's environment for each stage:
INSIGHTS_PROXY_TOKEN=ins_proxy_…Never put it in the page, and never in a setting that starts with NEXT_PUBLIC_: those are written into the browser code. The site key is not a secret, but the proxy token is.
3Add the proxy route (Next.js)
With the CMS Orbit SDK, one route does everything. Install shows it under Serve it from your own domain (Next.js), with a link to create the token:
import { createInsightsProxy } from "@bifrost-orbit-cms/sdk/next";export const { GET, POST } = createInsightsProxy();It answers these addresses on your domain:
/stats/script.js/insights/v1/script.jsGET/stats/collect/insights/v1/collectPOST/stats/labels, /stats/label.js/insights/v1/labels, /insights/v1/label.jsGET/stats/labelling/insights/v1/labellingPOST| Your address | Passed on to | Method |
|---|---|---|
/stats/script.js | /insights/v1/script.js | GET |
/stats/collect | /insights/v1/collect | POST |
/stats/labels, /stats/label.js | /insights/v1/labels, /insights/v1/label.js | GET |
/stats/labelling | /insights/v1/labelling | POST |
Anything else answers 404. It passes on Content-Type, User-Agent, Origin, DNT and Sec-GPC, never cookies. On collect, when INSIGHTS_PROXY_TOKEN is set and it finds the visitor's address, it adds X-Insights-Proxy-Token and X-Insights-Forwarded-For.
It reads the visitor's address from CF-Connecting-IP, then X-Real-IP, then the first entry of X-Forwarded-For. Your website must run behind a proxy or host that sets one of them, such as Cloudflare, Caddy, Nginx or Vercel; otherwise there is no address to pass on and visits carry your server's address.
createInsightsProxy takes options when the defaults don't fit:
tokenINSIGHTS_PROXY_TOKEN. Pass null to send no token.originINSIGHTS_ORIGIN, or https://api.bifrostorbit.com when it is unset or empty. Use https://api-stage.bifrostorbit.com for a site in Orbit's staging.| Option | Default |
|---|---|
token | INSIGHTS_PROXY_TOKEN. Pass null to send no token. |
origin | INSIGHTS_ORIGIN, or https://api.bifrostorbit.com when it is unset or empty. Use https://api-stage.bifrostorbit.com for a site in Orbit's staging. |
Both settings are read when the route first loads, so restart or redeploy the website after changing them.
4Load the script from your domain
Install shows both versions below with your site key filled in: the SDK component for a site connected to CMS Orbit, and the script tag for any website.
import { CmsInsights } from "@bifrost-orbit-cms/sdk/react";<CmsInsights site={process.env.INSIGHTS_SITE} src="/stats/script.js" api="/stats/collect" />Without the SDK, the script tag is:
<script defer data-site="SITE_KEY" data-api="/stats/collect" src="/stats/script.js"></script>Always set data-api (api in the SDK) with your own src. Without it, the script sends events to /insights/v1/collect on your domain, which your proxy doesn't answer.
With trailingSlash: true in next.config.ts, use /stats/collect/, so the browser isn't redirected.
NEXT_PUBLIC_ settings are fixed when the website is built. If you build once and deploy the same build to several stages, read the site key as an ordinary server setting when the page renders, as above, rather than from NEXT_PUBLIC_INSIGHTS_SITE.
Another server or framework
Any server that can pass on a request works. Serve script.js from https://api.bifrostorbit.com/insights/v1/script.js, and send each POST to your collect address on to https://api.bifrostorbit.com/insights/v1/collect with:
- the body unchanged
Content-Type,User-Agent,Origin,DNTandSec-GPCfrom the browser's request. WithoutUser-Agent, every visit is left out as a bot.X-Insights-Forwarded-For: the visitor's address, one IP address. With a list, the first entry is used.X-Insights-Proxy-Token: the token
Answer the browser with the status and body Insights Orbit gave. The CMS Orbit SDKs for PHP, Swift and Kotlin write the script tag with your own src and api, but don't include a proxy. See Connect Insights Orbit with the CMS Orbit SDK.
How Insights Orbit treats the headers
X-Insights-Proxy-TokenX-Insights-Forwarded-For is ignored. The connection's own address is used.403 and wrong_proxy_token: That proxy token is not this site's. Nothing from that request is counted.| Your request carries | What happens |
|---|---|
No X-Insights-Proxy-Token | X-Insights-Forwarded-For is ignored. The connection's own address is used. |
| The right token and a valid address | The forwarded address is used, and the rate limit counts per visitor. |
| The right token, but no valid address | The connection's own address is used. |
| A wrong token, or a token after it was replaced or removed | Refused with 403 and wrong_proxy_token: That proxy token is not this site's. Nothing from that request is counted. |
A wrong token is refused rather than ignored, so a mistake shows up at once. When you replace or remove the token, update or remove INSIGHTS_PROXY_TOKEN on your server straight away, or every batch is refused.
Check it works
- Open a page of your website with the browser's developer tools on the Network tab.
- Find the
POSTto/stats/collect. It should answer202with{ "data": { "accepted": 1 } }or more. - In Insights Orbit, Right now on the overview shows you within a few seconds, and Places shows your own country, not your server's.
accepted: 0 means the batch arrived but was left out on purpose, for example because your browser sends Do Not Track. See Troubleshooting.
Rewrites without a token
insightsRewrites() from @bifrost-orbit-cms/sdk/next, or rewrites in your own server, also serve the script from your domain, but can't add headers. Visits then carry your server's address, with the limits listed at the top. Use them only when the route above isn't possible, and switch the data-centre filter off under Site settings if your server runs in a data centre.
Content Security Policy
Through a proxy, the browser loads and sends everything from your own domain, so 'self' in script-src and connect-src is enough.
Was this page helpful?