Support/Proxy Guides
Proxy Guides

Proxying Flowsery Analytics with Astro

Route Flowsery Analytics through your Astro application using middleware to prevent adblocker interference and capture more accurate visitor data.

1. Create the Middleware

Create a middleware file at src/middleware.js (or src/middleware.ts for TypeScript projects):

JavaScript
// src/middleware.js
export async function onRequest(context, next) {
  const { request } = context;
  const url = new URL(request.url);
 
  // Proxy the Flowsery Analytics tracking script
  if (url.pathname === '/js/main.js') {
    const response = await fetch('https://cdn.flowsery.com/main.js');
    const script = await response.text();
 
    return new Response(script, {
      headers: {
        'Content-Type': 'application/javascript',
        'Cache-Control': 'public, max-age=31536000',
      },
    });
  }
 
  // Proxy the event collection endpoint
  if (url.pathname === '/api/track' && request.method === 'POST') {
    const body = await request.text();
 
    // Resolve the real visitor IP for accurate geolocation
    const clientIp =
      request.headers.get('cf-connecting-ip') || request.headers.get('x-real-ip') || request.headers.get('x-forwarded-for')?.split(',')[0] || '';
 
    const response = await fetch('https://analytics.flowsery.com/analytics/events', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'User-Agent': request.headers.get('User-Agent') || '',
        Origin: request.headers.get('Origin') || url.origin,
        // Flowsery reads `x-flowsery-real-ip` as the authoritative visitor IP.
        // Without it, every visitor resolves to your server's region because
        // Cloudflare rewrites `cf-connecting-ip` on the proxy hop.
        ...(clientIp && { 'x-flowsery-real-ip': clientIp }),
      },
      body: body,
    });
 
    const responseText = await response.text();
 
    return new Response(responseText, {
      status: response.status,
      headers: {
        'Content-Type': 'application/json',
      },
    });
  }
 
  return next();
}

2. Enable Server-Side Rendering in Astro

Update your astro.config.mjs to support SSR (required for middleware):

JavaScript
// astro.config.mjs
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
 
export default defineConfig({
  output: 'server', // or 'hybrid'
  adapter: node({
    mode: 'standalone',
  }),
});

You will also need the Node.js adapter: npm install @astrojs/node

3. Alternative: Static Site with API Routes

If you prefer keeping your site fully static, create API routes instead of middleware:

Script Proxy Route

Create src/pages/js/main.js.js:

JavaScript
// src/pages/js/main.js.js
export async function GET() {
  const response = await fetch('https://cdn.flowsery.com/main.js');
  const script = await response.text();
 
  return new Response(script, {
    headers: {
      'Content-Type': 'application/javascript',
      'Cache-Control': 'public, max-age=31536000',
    },
  });
}

Events Proxy Route

Create src/pages/api/track.js:

JavaScript
// src/pages/api/track.js
export async function POST({ request }) {
  const body = await request.text();
  const url = new URL(request.url);
 
  // Resolve the real visitor IP for accurate geolocation
  const clientIp =
    request.headers.get('x-real-ip') || request.headers.get('x-forwarded-for')?.split(',')[0] || request.headers.get('cf-connecting-ip') || '';
 
  const response = await fetch('https://analytics.flowsery.com/analytics/events', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'User-Agent': request.headers.get('User-Agent') || '',
      Origin: request.headers.get('Origin') || url.origin,
      'x-forwarded-for': clientIp,
    },
    body: body,
  });
 
  const responseText = await response.text();
 
  return new Response(responseText, {
    status: response.status,
    headers: {
      'Content-Type': 'application/json',
    },
  });
}

Note: If your project already uses an /api/track route, add data-api to the Flowsery Analytics script tag to redirect events elsewhere. For example, data-api="/flowsery-events" sends data to /flowsery-events instead.

Important: If every visitor appears to be in the same location in your dashboard, confirm that x-flowsery-real-ip is set to the real visitor IP (not the proxy server IP) when forwarding requests to the Flowsery Analytics /events endpoint.

4. Modify the Script Tag

Replace the original Flowsery Analytics snippet in your layout with the proxied version:

ASTRO
---
// src/layouts/Layout.astro
const { title } = Astro.props;
---
 
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <title>{title}</title>
    <script
      defer
      data-fl-website-id="flid_******"
      src="/js/main.js"
    ></script>
  </head>
  <body>
    <slot />
  </body>
</html>

5. Deploy

After deploying, the proxy configuration takes effect automatically. Verify that your hosting provider supports:

  • Server-side rendering (for the middleware approach)
  • API routes (for the static site approach)

Common Astro-compatible hosts include Vercel, Netlify, and Cloudflare Pages.

Confirming It Works

To validate that the proxy is functioning correctly:

  1. Navigate to your website
  2. Open your browser's developer tools and switch to the Network tab
  3. Verify that analytics requests are served from your domain rather than analytics.flowsery.com

Note: The middleware approach requires server-side rendering, whereas API routes work with both static and server-rendered sites.

Troubleshooting

Every visitor appears from the same location

When all visitors show a single geographic location (typically your server's region), the proxy is not forwarding real visitor IPs correctly.

Resolution:

  1. Ensure your proxy sets x-flowsery-real-ip to the actual visitor IP (not the server IP) when forwarding requests to the Flowsery Analytics /events endpoint.
  2. If your Astro deployment sits behind another proxy (Vercel, Cloudflare, Netlify), the IP you want is usually in cf-connecting-ip, x-vercel-forwarded-for, or x-real-ip — read whichever your edge provides first.