A/B Test with a Rewrite in proxy.ts
Hard · Proxy
In Next.js 16, middleware.ts was renamed to proxy.ts (exporting function proxy(request)). It runs before a route renders and can redirect, rewrite or set cookies. A rewrite renders a different route while the browser keeps the original URL, which makes it perfect for A/B tests: everyone visits /, but half of the visitors see /home-a and the other half /home-b.
The starter has small stand-ins for NextRequest / NextResponse (the real ones extend the Web Request / Response): request.nextUrl, request.cookies.get(name)?.value, NextResponse.next(), NextResponse.rewrite(url) and response.cookies.set(name, value, options). The percentage of visitors that get bucket a is passed as split so the tests can change it; in a real app it is a constant or comes from a feature-flag service.
- Only the exact path / takes part; every other path returns NextResponse.next(). (In a real app you would also limit this with export const config = { matcher: "/" }.)
- Requests whose user-agent contains bot (any case) always get /home-a and never a cookie, so crawlers index one stable page.
- A valid ab-home cookie (a or b) keeps the visitor in that bucket, without setting the cookie again.
- Otherwise pick the bucket from the x-visitor-id header (anonymous when it is missing): hash(id) % 100 < split means a, else b. Invalid cookie values (like c) count as missing. The same id always lands in the same bucket.
- Rewrite to /home-a or /home-b and keep the query string (/?utm=x → /home-b?utm=x). The URL must be absolute: new URL("/home-b" + search, request.url).
- When the bucket was just picked, set the cookie on the response: ab-home, with { path: "/", maxAge: 60 * 60 * 24 * 30, sameSite: "lax" } (30 days).