Proxy / Custom Domain
Ad blockers and privacy extensions block requests to known analytics domains like api.kitbase.dev. You can avoid this by proxying analytics requests through your own domain so they appear as first-party traffic.
Why proxy?
- Ad blockers -- requests to your own domain are not blocked
- Content Security Policy -- no need to allowlist a third-party domain
- Brand consistency -- all traffic stays on your domain
- Privacy -- analytics data never visibly leaves your infrastructure
How It Works
Browser ──► your-domain.com/kb/* ──► api.kitbase.dev/*
Browser ──► your-domain.com/lite.js ──► kitbase.dev/lite.jsYour proxy sits between the browser and the Kitbase API. The SDK sends requests to your proxy URL, and the proxy forwards them to Kitbase. Two things need proxying:
- API endpoints --
POST /sdk/v1/logs,POST /sdk/v1/logs/batch,POST /sdk/v1/identify - Tracking script (optional) -- the
lite.jsfile loaded via script tag
SDK Configuration
Point the SDK at your proxy by setting the baseUrl option.
NPM Package
import { init } from '@kitbase/analytics';
const kitbase = init({
sdkKey: 'YOUR_SDK_KEY',
baseUrl: 'https://your-domain.com/kb',
});Script Tag
<script>
window.KITBASE_CONFIG = {
sdkKey: 'YOUR_SDK_KEY',
baseUrl: 'https://your-domain.com/kb',
};
</script>
<script defer src="https://your-domain.com/lite.js"></script>The SDK sends all API requests to baseUrl instead of https://api.kitbase.dev. The x-sdk-key header is included automatically -- no changes needed on the auth side.
Proxy Setup Examples
Next.js (Rewrites)
Next.js rewrites require zero extra dependencies. Add this to your next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
async rewrites() {
return [
{
source: '/kb/:path*',
destination: 'https://api.kitbase.dev/:path*',
},
];
},
};
module.exports = nextConfig;Then set baseUrl to /kb (relative URL):
const kitbase = init({
sdkKey: 'YOUR_SDK_KEY',
baseUrl: '/kb',
});Node.js (Express)
Use http-proxy-middleware to forward requests:
npm install http-proxy-middlewareconst express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
app.use(
'/kb',
createProxyMiddleware({
target: 'https://api.kitbase.dev',
changeOrigin: true,
pathRewrite: { '^/kb': '' },
})
);
app.listen(3000);Nginx
location /kb/ {
proxy_pass https://api.kitbase.dev/;
proxy_set_header Host api.kitbase.dev;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_ssl_server_name on;
}PHP
A minimal curl-based proxy script:
<?php
// proxy.php — place at /kb/proxy.php and rewrite /kb/* to this file
$path = ltrim($_SERVER['PATH_INFO'] ?? '', '/');
$url = 'https://api.kitbase.dev/' . $path;
$headers = [];
foreach (getallheaders() as $key => $value) {
// Forward relevant headers
$lower = strtolower($key);
if (in_array($lower, ['content-type', 'x-sdk-key', 'user-agent'])) {
$headers[] = "$key: $value";
}
}
// Forward the client IP
$headers[] = 'X-Forwarded-For: ' . $_SERVER['REMOTE_ADDR'];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $_SERVER['REQUEST_METHOD']);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
curl_setopt($ch, CURLOPT_POSTFIELDS, file_get_contents('php://input'));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);
http_response_code($httpCode);
header('Content-Type: ' . ($contentType ?: 'application/json'));
echo $response;Cloudflare Workers
export default {
async fetch(request) {
const url = new URL(request.url);
// Rewrite /kb/* → api.kitbase.dev/*
url.hostname = 'api.kitbase.dev';
url.pathname = url.pathname.replace(/^\/kb/, '');
const headers = new Headers(request.headers);
headers.set('X-Forwarded-For', request.headers.get('CF-Connecting-IP'));
return fetch(url.toString(), {
method: request.method,
headers,
body: request.body,
});
},
};Self-Hosting the Script File
To also proxy the tracking script (lite.js), serve it from your domain. This prevents the script tag itself from being blocked.
Option A: Static file -- download https://kitbase.dev/lite.js and serve it as a static asset. Re-download periodically to pick up SDK updates.
Option B: Rewrite rule -- proxy the request like the API:
# Nginx
location = /lite.js {
proxy_pass https://kitbase.dev/lite.js;
proxy_set_header Host kitbase.dev;
proxy_ssl_server_name on;
proxy_cache_valid 200 1h;
}// next.config.js
{
source: '/lite.js',
destination: 'https://kitbase.dev/lite.js',
}Then load the script from your domain:
<script defer src="https://your-domain.com/lite.js"></script>Important Notes
Forward Client IP Headers
Kitbase uses client IP addresses for geolocation enrichment (country, region, city). Your proxy must forward the original client IP so geo data is accurate. The backend reads these headers in order:
CF-Connecting-IP(Cloudflare)X-Forwarded-For(standard proxy header)X-Real-IP(Nginx)True-Client-IP(Akamai)
Most reverse proxies add X-Forwarded-For automatically. If yours doesn't, add it explicitly (see the examples above).
Keep the User-Agent Header
The backend parses User-Agent to extract device type, browser, and OS. Make sure your proxy forwards this header unchanged.
Don't Cache POST Responses
The SDK sends analytics data via POST requests. Caching POST responses will cause events to be silently dropped. Only cache GET requests (like lite.js).
Update Your Content Security Policy
If you use CSP headers, update connect-src to allow your proxy path instead of (or in addition to) api.kitbase.dev:
script-src 'self';
connect-src 'self';Since both the script and API calls come from your own domain, no third-party domains need to be allowlisted.