Overview

Multi-tenant preview URLs

Test per tenant changes in preview deployments by adding dynamic prefixes to preview URLs.

Multi-tenant preview URLs let you test tenant-specific experiences in preview deployments without configuring additional domains. Add any prefix before --- in a preview URL, and Vercel routes the request to the same deployment while passing the full hostname to your code.

This feature requires a custom preview suffix suffix. It does not work with the default .vercel.app suffix.

The problem

Standard preview URLs like my-app-git-feature.vercel.dev work well for single-tenant applications, but multi-tenant apps need to test changes for each tenant separately.

Without tenant-aware previews, you would need to:

  • Manually switch tenant context in your application
  • Deploy separate preview environments per tenant
  • Manually assign domains to each preview deployment

The solution

You can add any dynamic prefix before --- in your preview URL:

{prefix}---{preview-url}

Vercel routes the request to the same deployment as {preview-url}, but your code receives the full hostname including the prefix. This lets you extract the prefix and handle tenant routing however you want.

Examples:

URLRoutes toYour code receives
acme---preview-xyz.vercel.devpreview-xyz.vercel.devhost: acme---preview-xyz.vercel.dev
globex---my-app-git-feature.vercel.devmy-app-git-feature.vercel.devhost: globex---my-app-git-feature.vercel.dev
tenant-123---my-app-abc123.vercel.devmy-app-abc123.vercel.devhost: tenant-123---my-app-abc123.vercel.dev

How it works

  1. User visits {tenant}---{preview-url}
  2. Vercel routes the request to the deployment at {preview-url}
  3. Your code receives the full hostname: {tenant}---{preview-url}
  4. Your code extracts the prefix and handles routing

The prefix can be anything—a tenant ID, workspace name, feature flag, or anything else your application needs.

Reference implementation

Below if a reference implementation of extracting a tenant prefix from the hostname and routing to the /[domain]/page.tsx path.

middleware.ts
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { getSubdomain } from "tldts";

export async function middleware(request: NextRequest) {
  const hostname = request.headers.get("host") || request.nextUrl.hostname;
  const subdomain = getSubdomain(hostname) || "";
  const [tenantPart] = subdomain.includes("---") ? subdomain.split("---") : [];

  if (!tenantPart) {
    return NextResponse.next();
  }
  const url = request.nextUrl.clone();
  const pathname = url.pathname;
  // Rewrite to tenant-prefixed path
  url.pathname = `/${tenantPart}${pathname}`;
  return NextResponse.rewrite(url);
}

export const config = {
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};

Checkout multi-tenant preview URLs in action with this demo.

Limitations

  • Preview URL prefixes only work with custom deployment URL suffixes, not the default .vercel.app
  • The prefix must appear before --- in the preview URL path
  • Total hostname length must not exceed DNS limits (253 characters)

On this page