Next.js Proxy Setup
Configure Next.js rewrites to proxy Rybbit tracking and bypass ad blockers
Next.js provides a powerful rewrites feature that makes it easy to proxy Rybbit tracking through your own domain. This guide works with both the App Router (Next.js 13+) and Pages Router.
Overview
Next.js rewrites allow you to map incoming request paths to different destination URLs transparently. This is perfect for proxying analytics since the browser sees requests to your domain while Next.js forwards them to Rybbit's servers behind the scenes.
What you'll achieve:
- Serve Rybbit scripts from your domain (e.g.,
/analytics/script.js) - Proxy all tracking requests through your Next.js application
- Bypass ad blocker detection with first-party requests
- Support all Rybbit features (session replay, Web Vitals, custom events)
Prerequisites
- Next.js 12 or later (rewrites are supported in all modern versions)
- Your Rybbit site ID (found in your dashboard)
- Your Rybbit instance URL:
- Cloud hosted:
https://app.rybbit.io - Self-hosted: Your instance URL (e.g.,
https://analytics.yourcompany.com)
- Cloud hosted:
Implementation
Configure Environment Variables
Create or update your .env or .env.local file with your Rybbit instance URL:
# .env.local
NEXT_PUBLIC_RYBBIT_HOST=https://app.rybbit.io# .env.local
NEXT_PUBLIC_RYBBIT_HOST=https://analytics.yourcompany.comReplace analytics.yourcompany.com with your actual Rybbit instance domain.
The NEXT_PUBLIC_ prefix makes this environment variable available in both server and client environments, though it's primarily used during build time for the rewrites configuration.
Set Up Rewrites in next.config.js
Add or update the rewrites function in your next.config.js (or next.config.mjs for ESM):
This configuration proxies all Rybbit endpoints including session replay and Web Vitals:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
async rewrites() {
const rybbitHost = process.env.NEXT_PUBLIC_RYBBIT_HOST;
if (!rybbitHost) {
console.warn('NEXT_PUBLIC_RYBBIT_HOST is not set. Rybbit proxy will not work.');
return [];
}
return [
// Tracking scripts
{
source: '/analytics/script.js',
destination: `${rybbitHost}/api/script.js`,
},
{
source: '/analytics/script-full.js',
destination: `${rybbitHost}/api/script-full.js`,
},
{
source: '/analytics/replay.js',
destination: `${rybbitHost}/api/replay.js`,
},
{
source: '/analytics/metrics.js',
destination: `${rybbitHost}/api/metrics.js`,
},
// Tracking endpoints
{
source: '/analytics/track',
destination: `${rybbitHost}/api/track`,
},
{
source: '/analytics/identify',
destination: `${rybbitHost}/api/identify`,
},
{
source: '/analytics/session-replay/record/:siteId',
destination: `${rybbitHost}/api/session-replay/record/:siteId`,
},
{
source: '/analytics/site/tracking-config/:siteId',
destination: `${rybbitHost}/site/tracking-config/:siteId`,
},
];
},
};
module.exports = nextConfig;Minimal setup with just script loading and basic event tracking:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
async rewrites() {
const rybbitHost = process.env.NEXT_PUBLIC_RYBBIT_HOST;
if (!rybbitHost) {
console.warn('NEXT_PUBLIC_RYBBIT_HOST is not set. Rybbit proxy will not work.');
return [];
}
return [
{
source: '/analytics/script.js',
destination: `${rybbitHost}/api/script.js`,
},
{
source: '/analytics/track',
destination: `${rybbitHost}/api/track`,
},
];
},
};
module.exports = nextConfig;This minimal setup won't support session replay or Web Vitals. Use the full proxy configuration if you need these features.
If you're using ES modules:
// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
async rewrites() {
const rybbitHost = process.env.NEXT_PUBLIC_RYBBIT_HOST;
if (!rybbitHost) {
console.warn('NEXT_PUBLIC_RYBBIT_HOST is not set. Rybbit proxy will not work.');
return [];
}
return [
{
source: '/analytics/script.js',
destination: `${rybbitHost}/api/script.js`,
},
{
source: '/analytics/track',
destination: `${rybbitHost}/api/track`,
},
{
source: '/analytics/replay.js',
destination: `${rybbitHost}/api/replay.js`,
},
{
source: '/analytics/metrics.js',
destination: `${rybbitHost}/api/metrics.js`,
},
{
source: '/analytics/identify',
destination: `${rybbitHost}/api/identify`,
},
{
source: '/analytics/session-replay/record/:siteId',
destination: `${rybbitHost}/api/session-replay/record/:siteId`,
},
{
source: '/analytics/site/tracking-config/:siteId',
destination: `${rybbitHost}/site/tracking-config/:siteId`,
},
];
},
};
export default nextConfig;Custom Path: You can change /analytics to any path you prefer (e.g., /tracking, /stats, /rybbit). Just ensure you use the same path consistently in both your rewrites and script tag.
Update Your Tracking Script
Update your script tag to load from the proxied path instead of directly from Rybbit:
Update your root layout file:
// app/layout.tsx
import Script from 'next/script';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
{children}
<Script
src="/analytics/script.js"
data-site-id="YOUR_SITE_ID"
strategy="afterInteractive"
/>
</body>
</html>
);
}Replace YOUR_SITE_ID with your actual site ID from the Rybbit dashboard.
Update your _app.tsx or _app.jsx file:
// pages/_app.tsx
import Script from 'next/script';
import type { AppProps } from 'next/app';
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
<Script
src="/analytics/script.js"
data-site-id="YOUR_SITE_ID"
strategy="afterInteractive"
/>
</>
);
}
export default MyApp;Replace YOUR_SITE_ID with your actual site ID from the Rybbit dashboard.
// app/layout.js or pages/_app.js
import Script from 'next/script';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
{children}
<Script
src="/analytics/script.js"
data-site-id="YOUR_SITE_ID"
strategy="afterInteractive"
/>
</body>
</html>
);
}Restart Your Development Server
After updating next.config.js, restart your Next.js development server for the changes to take effect:
# Stop the server (Ctrl+C) and restart
npm run dev
# or
yarn dev
# or
pnpm devVerify the Setup
Open your website in a browser with Developer Tools:
- Open the Network tab and filter by "analytics" or "script"
- Verify the script loads:
- You should see a request to
/analytics/script.js(notapp.rybbit.io) - Status should be
200 OK - The domain should be your own domain
- You should see a request to
- Verify tracking works:
- Navigate to another page or trigger an event
- You should see POST requests to
/analytics/track - All requests should show your domain, not
app.rybbit.io
- Check the Rybbit dashboard:
- Wait 1-2 minutes for data processing
- Verify your session appears in the real-time dashboard
How It Works
The Rybbit tracking script has a built-in auto-discovery mechanism. When it loads from:
<script src="/analytics/script.js" data-site-id="123"></script>It automatically extracts the analytics host from its own src attribute:
// Inside Rybbit's script
const scriptElement = document.currentScript;
const src = scriptElement.getAttribute('src');
const analyticsHost = src.split('/script.js')[0];
// Result: "" (empty string means same domain)This means all tracking requests automatically go to /analytics/track, /analytics/identify, etc., which Next.js then rewrites to forward to Rybbit's servers.
Performance Considerations
Bandwidth Usage
All tracking requests now flow through your Next.js server, which will increase bandwidth usage:
Bandwidth Impact: Each session typically uses 10-50 KB without session replay, or 50-500 KB with session replay enabled. For a site with 100,000 monthly sessions, expect 5-10 GB/month additional bandwidth without replay, or 30-80 GB/month with replay.
Most Vercel, Netlify, or other hosting plans can handle this, but check your plan's bandwidth limits if you have high traffic.
Caching
Next.js automatically caches responses when possible, but you can optimize further:
Using Vercel Edge Network: If deployed on Vercel, your rewrites benefit from the global Edge Network, which caches static scripts close to users worldwide.
Custom caching headers: The Rybbit server sets appropriate cache headers, which Next.js respects:
- Scripts (
.jsfiles): Cached for 1 hour - Configuration: Cached for 5 minutes
- Tracking POSTs: Not cached
Server Load
Rewrites are handled at the edge on Vercel, so there's minimal impact on your serverless functions. For self-hosted Next.js, consider:
- Using a CDN (Cloudflare, CloudFront, etc.) in front of Next.js
- Scaling your server if you have very high traffic
- Monitoring server response times
Troubleshooting
Script loads but events aren't tracked
Problem: You see the script load successfully, but no tracking data appears in the dashboard.
Solution:
- Check that
/analytics/trackis included in your rewrites - Verify POST requests to
/analytics/trackreturn200 OKin Network tab - Check for CORS errors in the browser console
- Ensure your Rybbit instance URL is correct in
.env.local
404 errors on tracking endpoints
Problem: Script loads fine, but tracking requests return 404.
Solution:
- Verify all tracking endpoints are in your
rewrites()configuration - Restart your dev server after changing
next.config.js - Clear your browser cache
- Check that the endpoint paths exactly match (case-sensitive)
Session replay not working
Problem: Basic tracking works but session replay doesn't record.
Solution: Add the session replay endpoints to your rewrites:
{
source: '/analytics/replay.js',
destination: `${rybbitHost}/api/replay.js`,
},
{
source: '/analytics/session-replay/record/:siteId',
destination: `${rybbitHost}/api/session-replay/record/:siteId`,
},Environment variable not loading
Problem: Rewrites don't work, console shows warning about missing NEXT_PUBLIC_RYBBIT_HOST.
Solution:
- Ensure you created
.env.localin your project root - Restart your dev server after creating
.env.local - For production, ensure environment variables are set in your hosting platform (Vercel, Netlify, etc.)
Deployment Considerations
Vercel
When deploying to Vercel:
- Add
NEXT_PUBLIC_RYBBIT_HOSTenvironment variable in your Vercel project settings - Rewrites automatically work at the edge with zero configuration
- Bandwidth is generous on all Vercel plans
Self-Hosted
When self-hosting Next.js:
- Ensure environment variables are available to the Next.js process
- Consider adding a CDN layer for better caching and reduced bandwidth
- Monitor server load if you have high traffic
Other Platforms (Netlify, AWS, etc.)
- Set environment variables in your platform's settings
- Verify rewrites are supported (most platforms support Next.js rewrites)
- Check bandwidth limits for your plan
Advanced Configuration
Different paths for different environments
You can customize the proxy path based on the environment:
// next.config.js
const nextConfig = {
async rewrites() {
const rybbitHost = process.env.NEXT_PUBLIC_RYBBIT_HOST;
const analyticsPath = process.env.NODE_ENV === 'development'
? '/dev-analytics'
: '/analytics';
return [
{
source: `${analyticsPath}/script.js`,
destination: `${rybbitHost}/api/script.js`,
},
// ... other rewrites
];
},
};Conditional proxy (development only)
If you only want proxying in production:
// next.config.js
const nextConfig = {
async rewrites() {
if (process.env.NODE_ENV === 'development') {
return []; // No proxy in development
}
// ... production rewrites
},
};Related Resources
- Next.js Integration Guide - General Next.js integration
- Tracking Script Documentation - Script configuration options
- Custom Events - Track custom events
- Next.js Rewrites Documentation - Official Next.js docs