Proxy guides
Firebase Hosting
Firebase Hosting cannot rewrite to another domain, so a Cloud Function serves the ProductLense script and forwards events behind your hosting rewrites.
Firebase Hosting rewrites can point only at your own Cloud Functions or Cloud Run services, not at another domain. So one small function does both jobs: it serves the script, and on the Scale plan it forwards events. Read the proxy overview first for what each one fixes.
The function
import { defineSecret } from 'firebase-functions/params';
import { onRequest } from 'firebase-functions/v2/https';
const PL_API_KEY = defineSecret('PL_API_KEY');
type Event = Record<string, unknown>;
export const productlense = onRequest({ secrets: [PL_API_KEY], region: 'us-central1' }, async (req, res) => {
if (req.method === 'GET' && req.path === '/js/a.js') {
const r = await fetch('https://api.productlense.app/a.js');
if (!r.ok) {
res.sendStatus(502);
return;
}
// Firebase Hosting's CDN caches this response, so the function runs about once a day per edge.
res.set('Cache-Control', 'public, max-age=86400').type('application/javascript').send(await r.text());
return;
}
if (req.method === 'POST' && req.path === '/m/api/e') {
const text = req.rawBody?.toString('utf8') ?? '';
if (text.length > 8192) {
res.sendStatus(413);
return;
}
let body: { b?: Event[] } & Event;
try {
body = JSON.parse(text);
} catch {
res.sendStatus(400);
return;
}
// Behind Firebase Hosting the visitor's address is the first x-forwarded-for entry.
const ip = String(req.headers['x-forwarded-for'] ?? '').split(',')[0]?.trim() ?? '';
const extra = { ip, ua: req.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 ${PL_API_KEY.value()}`, 'Content-Type': 'application/json' },
body: JSON.stringify(out),
});
res.set('Cache-Control', 'no-store').sendStatus(r.status);
return;
}
res.sendStatus(404);
});{
"hosting": {
"public": "public",
"rewrites": [
{ "source": "/js/a.js", "function": { "functionId": "productlense", "region": "us-central1" } },
{ "source": "/m/api/e", "function": { "functionId": "productlense", "region": "us-central1" } }
]
}
}Set the secret with firebase functions:secrets:set PL_API_KEY (a key with the events scope; see Authentication and API keys), then firebase deploy. Without the Scale plan, leave out the /m/api/e rewrite and use only the script.
Use it
<script defer data-site="SITE_ID" data-api="https://api.productlense.app" src="/js/a.js"></script><script defer data-site="SITE_ID" data-api="https://example.com/m" src="/js/a.js"></script>Check it works
- Open
https://example.com/js/a.js: it returns JavaScript. - Load a page with the developer tools open. The Network tab shows a
POSTtoapi/ewith status204. - Open the Live page in the ProductLense dashboard. Your visit appears within a few seconds.