← Back to Architecture Patterns
Architecture Pattern

🏢 Multi-Tenant SaaS Platform

Host hundreds or thousands of customer websites under custom domains — all from a single Next.js deployment. Wildcard domains + Edge Middleware identify tenants, Edge Config stores branding, and ISR caching isolates content per tenant.

Why This Pattern?

🏗️ One Codebase, All Tenants

A single Next.js deployment serves hundreds or thousands of branded sites. No per-tenant hosting, no infrastructure multiplication.

🌐 Custom Domains at Scale

Vercel's Domains API adds/removes domains programmatically. Each tenant gets client-a.com pointing to your single deployment.

🎨 Instant Tenant Customization

Edge Config stores themes (colors, fonts, logos). Update a tenant's brand in seconds — no redeployment needed.

🔒 Tenant-Isolated Caching

ISR cache keys include the hostname. client-a.com never sees client-b.com's cached content. Security by architecture.

Architecture Layers

Domain Routing (Edge Middleware)

Matches incoming hostname (client-a.com, client-b.com) to a tenantId. Reads from Edge Config or KV for tenant identification. Sets x-tenant-id header for downstream use.

Tenant Config Store

Edge Config for themes, feature flags, and branding (near-zero latency). Database for full tenant profiles, billing, permissions.

Dynamic Rendering

Single Next.js codebase renders pages based on tenant config — theme colors, logos, fonts, content, features. ISR cache keys include hostname for tenant isolation.

CMS (Tenant-Scoped)

Shared headless CMS with tenant-scoped content. Each tenant manages their own pages via CMS dashboard.

Domain Management (Vercel API)

Programmatic custom domain assignment via Vercel's Domains API. Tenants add their domain → CNAME to Vercel → auto SSL.

Tenant Admin Dashboard

SSR authenticated dashboard for managing content, settings, branding, and analytics per tenant.

How Tenant Routing Works

// middleware.ts — runs on every request at the edge
import { NextResponse } from 'next/server';
import { get } from '@vercel/edge-config';

export async function middleware(request) {
  const hostname = request.headers.get('host') || '';
  
  // Look up tenant from Edge Config (< 1ms read)
  const tenantConfig = await get(hostname);
  
  if (!tenantConfig) {
    return NextResponse.redirect(new URL('/not-found', request.url));
  }
  
  // Pass tenant info downstream via headers
  const response = NextResponse.next();
  response.headers.set('x-tenant-id', tenantConfig.id);
  response.headers.set('x-tenant-theme', tenantConfig.theme);
  return response;
}

// Adding a custom domain programmatically:
// POST https://api.vercel.com/v10/projects/{projectId}/domains
// { "name": "client-a.com" }
// → Customer adds CNAME: cname.vercel-dns.com
// → Vercel auto-provisions SSL

⚠️ Critical: Cache Isolation

The #1 security concern:Without proper cache isolation, Tenant A can see Tenant B's content. ISR cache keys must include the hostname or tenantId.

❌ Wrong: No tenant in cache key

ISR page cached as /homepage → shared across all tenants

✅ Correct: Tenant in cache key

Middleware sets x-tenant-id header → Next.js includes it in cache key automatically

Customer Results

Durable3M+ businesses

AI-powered website builder serving millions of small business sites on a single Vercel deployment. Custom domains via Domains API.

Mintlify1000s of doc domains

Developer documentation platform hosting thousands of custom docs domains. Each customer gets docs.their-company.com.

HashnodeCustom blog domains

Developer blogging platform where each writer gets their own custom domain — all routed through one Vercel project.