Multi-Tenant Platform Concepts
A tenant represents a customer, workspace, or organization within your multi-tenant application. Each tenant has its own data, configuration, and branding, but all tenants share the same codebase and deployment.
Examples:
- Blog platform: Each writer with their own blog is a tenant
- Documentation platform: Each company with its own docs site is a tenant
- E-commerce platform: Each store owner is a tenant
You can identify tenants using three approaches:
Subdomain-based: Extract the tenant from the subdomain (tenant1.yourapp.com)
const hostname = request.headers.get('host');
const subdomain = hostname.split('.')[0]; // "tenant1"Custom domain-based: Map custom domains to tenants (tenant1.com → Tenant 1)
// Map custom domain to tenant in database
const tenant = await db.tenant.findFirst({
where: { customDomain: hostname },
});Path-based: Extract tenant from URL path (/tenant1/dashboard)
const pathname = request.nextUrl.pathname;
const tenantSlug = pathname.split('/')[1]; // "tenant1"Multi-tenant applications must isolate data between tenants:
Database-level: Use tenant ID in all queries
const posts = await db.post.findMany({
where: { tenantId: tenant.id },
});Application-level: Next.js Proxy ensures requests can only access their tenant's data
Global Config: Store tenant configuration for fast lookups at the edge
Wildcard domains let you automatically serve all subdomains from a single Edgenivo project:
- Add
*.yourapp.comto your project - Point your domain to Edgenivo's nameservers
- Any first-level subdomain (
tenant1.yourapp.com,tenant2.yourapp.com) automatically routes to your app - Edgenivo issues one wildcard SSL certificate for
*.yourapp.comthat covers every first-level subdomain
Requirements: Edgenivo must be able to answer the DNS challenge for the wildcard certificate. Use Edgenivo's nameservers (ns1.vercel-dns.com, ns2.vercel-dns.com), or delegate certificate validation if you can't change nameservers.
Custom domains let tenants bring their own domain:
- Add
tenant1.comto your Edgenivo project via SDK - Tenant configures DNS (CNAME or nameservers)
- Verify domain ownership (TXT record)
- Edgenivo issues SSL certificate automatically
Edgenivo automatically issues SSL certificates for all domains using Let's Encrypt:
- Wildcard domains: One wildcard certificate per wildcard domain, validated with a DNS challenge.
*.yourapp.comcoverstenant1.yourapp.combut notdocs.tenant1.yourapp.com; add*.tenant1.yourapp.comfor that level - Custom domains: Individual certificate per domain, validated with an HTTP challenge once the domain points to Edgenivo
- Automatic renewal before expiration
- No configuration required
For domains already in use on Edgenivo, ownership verification is required:
- Add domain to your project
- Edgenivo generates a unique TXT record
- Tenant adds TXT record to their DNS
- Verify ownership via SDK or dashboard
- Certificate issues once verified
Next.js Proxy runs on every request before your pages render:
export async function proxy(request: NextRequest) {
const hostname = request.headers.get('host');
// Get tenant from subdomain or custom domain
const tenant = await resolveTenant(hostname);
// Forward tenant context to your app on the request headers
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-tenant-id', tenant.id);
return NextResponse.next({
request: { headers: requestHeaders },
});
}- User visits
tenant1.yourapp.com - Request hits Edgenivo's CDN
- Proxy extracts subdomain (
tenant1) - Proxy looks up tenant in database or Global Config
- Proxy adds tenant context to the request headers
- Page component reads tenant from headers
- Page renders with tenant-specific data
Global Config: Store tenant configuration at the edge for sub-10ms lookups
import { get } from '@vercel/global-config';
const tenant = await get(`tenant_${hostname}`);Caching: Cache tenant lookups in the proxy to reduce database queries
Connection pooling: Use connection pooling for database queries to handle multiple tenants efficiently
Multi-tenant architecture means:
- One Next.js codebase
- One Edgenivo deployment
- Multiple domains (subdomains + custom domains)
- Shared infrastructure and resources
- Tenant-aware routing and data access
Pass tenant information through your application:
In Proxy: Forward context on the request headers
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-tenant-id', tenant.id);
return NextResponse.next({
request: { headers: requestHeaders },
});Use NextResponse.next({ request: { headers } }) to send the value to your app. Setting response.headers instead sends the header to the browser, where server components can't read it. Delete or overwrite inbound x-tenant-* headers on every path through the proxy so clients can't supply tenant context themselves.
In server components: Read headers
import { headers } from 'next/headers';
const headersList = await headers();
const tenantId = headersList.get('x-tenant-id');In API routes: Access request headers
const tenantId = request.headers.get('x-tenant-id');Was this helpful?