# Proxy with Firebase Hosting

> Rewrite two Firebase Hosting paths to a Cloud Function that relays MetricRaven.

Source: https://metricraven.com/docs/proxy-firebase  
Markdown: https://metricraven.com/docs/proxy-firebase.md  
Section: Proxy

Firebase Hosting rewrites your two paths to a Cloud Function, which forwards the visitor's IP.

## Set it up

1. **Add the function**

   Cloud Functions need the Blaze (pay as you go) plan. Use Node.js 18 or later for the built-in `fetch`.

   ```javascript title="functions/index.js"
   // functions/index.js · Cloud Functions for Firebase (2nd gen)
   const { onRequest } = require("firebase-functions/v2/https");

   exports.metricraven = onRequest({ region: "us-central1" }, async (req, res) => {
     const script = req.path === "/a/script" && req.method === "GET";
     const event = req.path === "/a/event" && req.method === "POST";
     if (!script && !event) return res.status(404).end();
     const headers = {};
     for (const name of ["content-type", "origin", "user-agent"]) {
       if (req.headers[name]) headers[name] = req.headers[name];
     }
     // Hosting's CDN puts the visitor's address here; confirm it on your project (see below).
     const ip = req.headers["fastly-client-ip"] || req.headers["x-forwarded-for"]?.split(",")[0]?.trim();
     if (ip) headers["x-visitor-ip"] = ip;
     try {
       const upstream = await fetch(script ? "https://metricraven.com/js/script.js" : "https://metricraven.com/api/v1/collect", {
         method: req.method,
         headers,
         body: event ? req.rawBody : undefined,
         signal: AbortSignal.timeout(15000),
       });
       res
         .status(upstream.status)
         .set("Content-Type", upstream.headers.get("content-type") || "application/octet-stream")
         .set("Cache-Control", script ? "public, max-age=3600" : "no-store")
         .send(Buffer.from(await upstream.arrayBuffer()));
     } catch {
       res.status(502).end();
     }
   });
   ```

2. **Rewrite the paths to it**

   Add the two rewrites to `firebase.json`, above any `**` catch-all, and keep the region the same as the function's:

   ```json title="firebase.json"
   {
     "hosting": {
       "public": "public",
       "rewrites": [
         { "source": "/a/script", "function": { "functionId": "metricraven", "region": "us-central1" } },
         { "source": "/a/event", "function": { "functionId": "metricraven", "region": "us-central1" } }
       ]
     }
   }
   ```

3. **Deploy**

   ```bash
   firebase deploy --only functions,hosting
   ```

4. **Point your script tag at your own paths**

   Replace your MetricRaven tag with this one. `src` loads the tracker from your domain and `data-api` sends its events there too.

   ```html title="Your website's shared layout"
   <script defer src="/a/script" data-api="/a/event" data-site-id="YOUR_SITE_ID"></script>
   ```

> **Verify it works**
>
> Open your live site. In the browser's Network panel, `/a/script` loads the tracker and `/a/event` answers with `ok: true`. Nothing goes to `metricraven.com` from the browser.
>
> In **Traffic**, your own visit should show your real country and city. If every visit appears to come from your server's or host's location, the visitor IP isn't being forwarded.

## Notes

- Firebase doesn't document which header carries the visitor IP through a Hosting rewrite. The function reads `fastly-client-ip`, then the first `x-forwarded-for` entry. After deploying, confirm your own visit shows your city; if not, log `req.headers` once and pick the header with your IP.
- A Hosting rewrite with a `destination` URL can't forward to another domain with a per-visitor header, which is why a function is needed.

How the proxy works and which headers it must send: [Use your own domain](https://metricraven.com/docs/proxy).

## Details

- The proxy forwards only `Content-Type`, `Origin`, `User-Agent` and the visitor's IP as `x-visitor-ip`. Your site's cookies and auth headers never leave your server.
- `Origin` matters: MetricRaven checks it against your site's allowed domains. A missing or wrong origin is refused with `domain_not_allowed`.
- MetricRaven allows about 120 events per minute per visitor IP. Without `x-visitor-ip`, all of your visitors share the proxy's IP, so busy sites lose events as well as locations.
- A proxy doesn't guarantee that every blocker will allow tracking.
