Install on Next.js
The choice is stored on your own domain, so Safari keeps it for its full lifetime, and the regional rules (UK, EU, US) use the visitor's real country.
1.Route /_tc to the banner
Add a proxy file next to your app folder. It forwards the visitor's IP and country (and US state on Vercel), so proof of consent and the regional rules are right.
// src/proxy.ts (Next.js 16; on Next.js 15 or earlier name it middleware.ts and the function middleware) import { NextResponse, type NextRequest } from "next/server"; const CMP_EDGE = "https://cmp.trackable.digital"; export function proxy(request: NextRequest) { const url = new URL(request.nextUrl.pathname + request.nextUrl.search, CMP_EDGE); const headers = new Headers(request.headers); const ip = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || request.headers.get("x-real-ip") || ""; // The visitor's country and state, from Cloudflare or Vercel in front of the site const country = request.headers.get("cf-ipcountry") || request.headers.get("x-vercel-ip-country") || ""; const region = request.headers.get("x-vercel-ip-country-region") || ""; headers.set("x-forwarded-host", request.headers.get("host") ?? request.nextUrl.host); headers.set("x-forwarded-proto", "https"); if (ip) headers.set("x-tc-client-ip", ip); if (country) headers.set("x-tc-country", country); if (region) headers.set("x-tc-region", region); headers.delete("cookie"); headers.set("accept-encoding", "identity"); // a compressed answer can arrive empty through a proxy return NextResponse.rewrite(url, { request: { headers } }); } export const config = { matcher: ["/_tc/:path*"] };2.Add the banner as the first script in <head>
In app/layout.tsx, inside <head>, before anything else. Use a plain <script>, not next/script, which would load it too late. It loads from our host (?so=1) because your CDN does not pass the visitor's country; choices still go through /_tc.
<head> {/* eslint-disable-next-line @next/next/no-sync-scripts */} <script src="https://cmp.trackable.digital/l/YOUR_SITE_KEY.js?so=1" /> </head>3.Allow it in your Content Security Policy
If the site sends a Content-Security-Policy, add https://cmp.trackable.digital to script-src and connect-src.
4.Remove the other cookie banner, keep GTM
Remove any other cookie banner or consent plugin. Keep Google Tag Manager, but make sure the banner script comes before it in <head>, including GTM added by a plugin, a module or a component.
5.Deploy and check
Open the site in a private window: the banner shows. Then press Check the site now on the site's Install page in the dashboard, and Scan now on the Cookies page to list its cookies.
Your site key
Replace YOUR_SITE_KEY with your site's key. The dashboard's Install page shows these same steps with it filled in.
Next: Google Tag Manager