# Proxy with Vue or Nuxt

> Proxy the MetricRaven script and events in the Vite dev server for Vue, or with a Nuxt server route.

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

Nuxt has a server, so a server route does the job. A Vite-built Vue app is static: proxy it on your host, and use the dev server proxy locally.

## Set it up

1. **Nuxt: add a server route**

   ```typescript title="server/routes/a/[kind].ts"
   // server/routes/a/[kind].ts · Nuxt 3 or 4 (Nitro server)
   export default defineEventHandler(async (event) => {
     const kind = getRouterParam(event, "kind");
     const script = kind === "script" && event.method === "GET";
     const isEvent = kind === "event" && event.method === "POST";
     if (!script && !isEvent) throw createError({ statusCode: 404 });
     const headers = new Headers();
     for (const name of ["content-type", "origin", "user-agent"]) {
       const value = getRequestHeader(event, name);
       if (value) headers.set(name, value);
     }
     // Reads X-Forwarded-For: trust it only if your host overwrites it.
     const ip = getRequestIP(event, { xForwardedFor: true });
     if (ip) headers.set("x-visitor-ip", ip);
     const upstream = await fetch(script ? "https://metricraven.com/js/script.js" : "https://metricraven.com/api/v1/collect", {
       method: event.method,
       headers,
       body: isEvent ? await readRawBody(event, false) : undefined,
       signal: AbortSignal.timeout(15000),
     });
     return new Response(upstream.body, {
       status: upstream.status,
       headers: {
         "Content-Type": upstream.headers.get("content-type") || "application/octet-stream",
         "Cache-Control": script ? "public, max-age=3600" : "no-store",
       },
     });
   });
   ```

   Nuxt auto-imports the h3 helpers. Routes under `server/routes` have no `/api` prefix.

2. **Nuxt: update the script in nuxt.config.ts**

   In `app.head.script`, set `src: "/a/script"` and add `"data-api": "/a/event"`.

3. **Vue (Vite): proxy on your host in production**

   `vite build` outputs static files, so the production proxy lives on your host: [Netlify](https://metricraven.com/docs/proxy-netlify), [Vercel](https://metricraven.com/docs/proxy-vercel), [Cloudflare Workers](https://metricraven.com/docs/proxy-cloudflare-workers), [Nginx](https://metricraven.com/docs/proxy-nginx) or [Caddy](https://metricraven.com/docs/proxy-caddy).

4. **Vue (Vite), optional: the same paths in development**

   So the proxied tag also works on `npm run dev`, add the dev server proxy. It only runs locally, and local visits are tracked only with `data-allow-localhost`.

   ```typescript title="vite.config.ts"
   // vite.config.ts · the Vite dev server only (vite build output has no server)
   import { defineConfig, type ProxyOptions } from "vite";

   const forwardVisitorIp: ProxyOptions["configure"] = (proxy) =>
     proxy.on("proxyReq", (proxyReq, req) => {
       proxyReq.setHeader("x-visitor-ip", (req.socket.remoteAddress || "").replace(/^::ffff:/, ""));
       proxyReq.removeHeader("cookie");
     });

   export default defineConfig({
     server: {
       proxy: {
         "/a/script": {
           target: "https://metricraven.com",
           changeOrigin: true,
           rewrite: () => "/js/script.js",
           configure: forwardVisitorIp,
         },
         "/a/event": {
           target: "https://metricraven.com",
           changeOrigin: true,
           rewrite: () => "/api/v1/collect",
           configure: forwardVisitorIp,
         },
       },
     },
   });
   ```

5. **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

- Nuxt's `routeRules` proxy option can't add a per-visitor header, so it isn't enough on its own. Use the server route.

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.
