Proxy guides
Next.js
Serve the ProductLense script from your Next.js domain with a rewrite, and forward events through a route handler on the Scale plan.
Next.js can serve the script from your own domain with one rewrite. On the Scale plan, a route handler forwards events too, so every request the browser makes is first-party. Read the proxy overview first for what each option fixes.
Serve the script
/** @type {import('next').NextConfig} */
module.exports = {
async rewrites() {
return [{ source: '/js/a.js', destination: 'https://api.productlense.app/a.js' }];
},
};Then load it from your path and keep events going to ProductLense with data-api:
import Script from 'next/script';
import type { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script
src="/js/a.js"
data-site="SITE_ID"
data-api="https://api.productlense.app"
strategy="afterInteractive"
/>
</body>
</html>
);
}Forward events (Scale plan)
- Create an API key with the
eventsscope (see Authentication and API keys) and store it asPL_API_KEYin your environment. - Add the route handler below.
- Change the tag to
data-api="https://example.com/m", using your own domain.
import type { NextRequest } from 'next/server';
type Event = Record<string, unknown>;
export async function POST(req: NextRequest) {
const text = await req.text();
if (text.length > 8192) return new Response(null, { status: 413 });
let body: { b?: Event[] } & Event;
try {
body = JSON.parse(text);
} catch {
return new Response(null, { status: 400 });
}
// The first x-forwarded-for entry is the visitor on Vercel and most hosts; adjust for your load balancer.
const ip = req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? '';
const extra = { ip, ua: req.headers.get('user-agent') ?? '' };
const out = body.b ? { ...body, b: body.b.map((e) => ({ ...e, ...extra })) } : { ...body, ...extra };
const r = await fetch('https://api.productlense.app/api/v1/events', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.PL_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify(out),
});
return new Response(null, { status: r.status });
}The browser sends batches as text/plain through sendBeacon, so the handler reads the raw text instead of req.json().
Check it works
- Open
https://example.com/js/a.js: it returns JavaScript, not your 404 page. - Load any page with the developer tools open. The Network tab shows
a.jsfrom your domain and aPOSTtoapi/ewith status204. - Open the Live page in the ProductLense dashboard. Your visit appears within a few seconds, with your real country.