Astro proxy

Proxy attribu.tech analytics through your Astro application using middleware.

See Proxy the tracking script for an overview of why and how proxying works.

Step 1: Create middleware

Create src/middleware.js (or .ts) to intercept requests and proxy them to attribu.tech:

export async function onRequest({ request, url }, next) {
  // Proxy the tracking script
  if (url.pathname === "/js/script.js" && request.method === "GET") {
    const res = await fetch("https://attribu.tech/js/script.js");
    return new Response(res.body, {
      status: res.status,
      headers: {
        "Content-Type": "application/javascript",
        "Cache-Control": "public, max-age=3600",
      },
    });
  }

  // Proxy the event API
  if (url.pathname === "/api/proxy/events" && request.method === "POST") {
    const body = await request.text();
    const res = await fetch("https://attribu.tech/api/events", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Forwarded-For":
          request.headers.get("x-forwarded-for") ||
          request.headers.get("cf-connecting-ip") ||
          "",
      },
      body,
    });
    return new Response(res.body, {
      status: res.status,
      headers: { "Content-Type": "application/json" },
    });
  }

  return next();
}

Step 2: Configure Astro for server output

Middleware requires server-side rendering. Update astro.config.mjs:

import { defineConfig } from "astro/config";
import node from "@astrojs/node";

export default defineConfig({
  output: "server",
  adapter: node({
    mode: "standalone",
  }),
});
Install the Node adapter if you haven't already: npx astro add node

Alternative: Static site with API routes

If you prefer a static Astro site with output: "hybrid", you can use API routes instead of middleware. Create these two files:

src/pages/js/script.js.js:

export async function GET() {
  const res = await fetch("https://attribu.tech/js/script.js");
  return new Response(res.body, {
    status: res.status,
    headers: {
      "Content-Type": "application/javascript",
      "Cache-Control": "public, max-age=3600",
    },
  });
}

export const prerender = false;

src/pages/api/proxy/events.js:

export async function POST({ request }) {
  const body = await request.text();
  const res = await fetch("https://attribu.tech/api/events", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Forwarded-For":
        request.headers.get("x-forwarded-for") ||
        request.headers.get("cf-connecting-ip") ||
        "",
    },
    body,
  });
  return new Response(res.body, {
    status: res.status,
    headers: { "Content-Type": "application/json" },
  });
}

export const prerender = false;

Step 3: Update your script tag

Point the script src to your proxied path and set data-api-url to your proxied API endpoint:

<script
  defer
  data-website-id="YOUR_SITE_ID"
  data-domain="yourdomain.com"
  data-api-url="/api/proxy/events"
  src="/js/script.js"
></script>
Replace YOUR_SITE_ID with your actual Website ID from attribu.tech. Replace yourdomain.com with your website's root domain.

Verify installation

  1. Visit your site in an incognito/private window.
  2. Open your browser's Developer Tools and go to the Network tab.
  3. Confirm the script loads from /js/script.js on your domain.
  4. Confirm the pageview POST goes to /api/proxy/events on your domain.
  5. Check your attribu.tech dashboard. A new pageview should appear within a few seconds.

Troubleshooting

  • All visitors show the same location - Make sure your middleware or API route forwards the visitor's IP via X-Forwarded-For. If deployed behind Cloudflare, use cf-connecting-ip.
  • Middleware not running - Confirm your Astro config uses output: "server" or output: "hybrid". Static builds do not execute middleware.
  • API routes returning 404 - Ensure each API route file exports prerender = false so Astro handles them at runtime.

What's next

Copied