Bypass ad blockers for Vercel Analytics in Next.js

If you're using Vercel Analytics with Next.js, chances are you're losing half of your analytics data. Why? Most modern browsers come with built-in ad blockers or tracking protection, which block requests from known analytics services, including Vercel Analytics.

ERR_BLOCKED_BY_CLIENT errors in the console

When you encounter ERR_BLOCKED_BY_CLIENT errors in your console, it means an ad blocker is at work. In this case, it's blocking Vercel's Analytics and Speed Insights scripts.

Ad blocker blocking a request

The request to /_vercel/insights/script.js are blocked because it's in the filter list. Consequently, the script doesn't load, and analytics data isn't sent.

The Solution

You can prevent Vercel Analytics from being blocked by making the analytics request URLs less recognizable. One simple method is using Next.js config's rewrites.

Step 1: Create a Rewrite Rule

next.config.js
1module.exports = { 2 rewrites: async () => { 3 return [ 4 { 5 source: "/api/data/:match*", 6 destination: "https://stepanpavlov.com/_vercel/insights/:match*", 7 }, 8 ]; 9 }, 10};

Rewrite all requests from /api/data/* to /_vercel/insights/*. Ensure the destination URL includes your domain name as that's where analytics requests go in production. The blocker will not notice requests to /api/data/*, while Next.js will handle them and forward them to the actual analytics script.

Step 2: Create a Custom Analytics Script

Inject a custom script to send requests to our rewritten endpoint. Set the data-endpoint attribute in the second script to the custom endpoint we've created in the rewrite rule.

lib/analytics.tsx
1import Script from "next/script"; 2 3export function VercelAnalytics() { 4 const isDev = process.env.NODE_ENV === "development"; 5 6 const src = isDev 7 ? "https://va.vercel-scripts.com/v1/script.debug.js" 8 : "/api/data/script.js"; 9 10 return ( 11 <> 12 <Script id="analytics" strategy="afterInteractive"> 13 {`window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };`} 14 </Script> 15 <Script async data-endpoint="/api/data" src={src} strategy="lazyOnload" /> 16 </> 17 ); 18}

This component loads the Vercel Analytics script from our custom endpoint /api/data/script.js. In development mode, it loads the debug script from Vercel's CDN to test analytics locally without affecting production data.

This solution has not been tested with the custom events feature. You may need to adjust the rewrites and the script to work with custom events.

Step 3: Include the Custom Script in Your Layout

app/layout.tsx
1import { VercelAnalytics } from "@lib/analytics"; 2 3export default function RootLayout({ 4 children, 5}: { 6 children: React.ReactNode; 7}) { 8 return ( 9 <html lang="en"> 10 <body> 11 {children} 12 <VercelAnalytics /> 13 </body> 14 </html> 15 ); 16}

Step 4: Deploy and Test

Deploy your changes and test if analytics data is sent correctly. Check the network tab in your browser's developer tools for requests sent to the correct endpoint. Adjust the custom endpoint if requests are still blocked.

If you had @vercel/analytics previously installed, you can safely uninstall it as the script now loads from a custom endpoint.

Bonus Tip: Adding Vercel's Speed Insights

Enabling Vercel's Speed Insights with the same method is as simple as adding another rewrite rule and a custom script.

Add rewrite rules for Speed Insights:

next.config.js
1module.exports = { 2 rewrites: async () => { 3 return [ 4 { 5 source: "/api/data/:match*", 6 destination: "https://stepanpavlov.com/_vercel/insights/:match*", 7 }, 8 { 9 source: "/api/performance/:match*", 10 destination: "https://stepanpavlov.com/_vercel/speed-insights/:match*", 11 }, 12 ]; 13 }, 14};

Define a custom script to load the Speed Insights script from the custom endpoint. Make sure the data-endpoint attribute ends with /vitals to match the Speed Insights script's default endpoint.

lib/speed-insights.tsx
1import Script from "next/script"; 2 3export function VercelSpeedInsights() { 4 const isDev = process.env.NODE_ENV === "development"; 5 6 const src = isDev 7 ? "https://va.vercel-scripts.com/v1/speed-insights/script.debug.js" 8 : "/api/performance/script.js"; 9 10 return ( 11 <> 12 <Script id="web-vitals" strategy="afterInteractive"> 13 {`window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); };`} 14 </Script> 15 <Script 16 async 17 data-endpoint="/api/performance/vitals" 18 src={src} 19 strategy="lazyOnload" 20 /> 21 </> 22 ); 23}

And finally, include the custom script in your layout.

app/layout.tsx
1import { VercelAnalytics } from "@lib/analytics"; 2import { VercelSpeedInsights } from "@lib/speed-insights"; 3 4export default function RootLayout({ 5 children, 6}: { 7 children: React.ReactNode; 8}) { 9 return ( 10 <html lang="en"> 11 <body> 12 {children} 13 <VercelAnalytics /> 14 <VercelSpeedInsights /> 15 </body> 16 </html> 17 ); 18}

This method can be adapted for other frameworks or analytics services by rewriting requests to a custom endpoint and loading scripts from there.