Path-based routing
Path-based routing
Route tenants using URL path prefixes in a multi-tenant Makeswift application.
Path-based routing uses the first segment of the URL path to identify the tenant for public visitors. For example, example.com/siteA/products serves the products page for Site A, while the bare root domain (example.com) serves the default tenant.
This is a hybrid approach: public visitors are identified by the URL path, while the Visual Builder connects via subdomain (because a Makeswift host URL is an origin only and cannot contain a path). Both styles render the same tenant content.
Before following this guide, complete the common configuration steps.
How it works
The middleware resolves the tenant in three steps:
- Subdomain first. If the host header has a valid tenant subdomain (for example,
siteA.localhost:3000), rewrite to/siteA/.... This is how the Visual Builder connects. - Path fallback. If the first path segment is already a valid tenant (for example,
/siteA/products), pass it through unchanged. - Default. Otherwise — the bare root domain or any unrecognized host — prepend the
defaulttenant.
Add the middleware
Create a middleware.ts file at the root of your project:
Makeswift API routes (/api/makeswift/*) are excluded via config.matcher — the API handler resolves the tenant from the host itself.
Configure the Visual Builder host URLs
Path-based URLs don’t carry tenant information in the host header, so the Visual Builder cannot connect using a path URL alone. Each Makeswift site needs its host URL set to the subdomain URL.
In the Makeswift dashboard, open the settings for each site and set the host URL:
For production, replace localhost:3000 with your deployed domain (for example, siteA.example.com).
Test the setup
Start the development server and verify each tenant loads:
Both path and subdomain URLs work for public viewing:
Path URLs work everywhere. For the subdomain URLs (and the Visual Builder), most browsers (Chrome, Firefox, Edge) resolve *.localhost to 127.0.0.1 automatically. Safari does not — see Resolving subdomains on Safari below.
Production deployment
The same logic works in production with two changes:
- Set
ROOT_DOMAINto your real domain (for example,ROOT_DOMAIN=example.com). Tenant subdomains are resolved relative to this value, and the bare apex domain maps to the default tenant. - Point each tenant subdomain at your deployment. Path-based public URLs (
example.com/siteA) work without any DNS changes, but the subdomain URLs and the Visual Builder require each tenant subdomain to resolve. Configure wildcard DNS (*.example.com) or an individual DNS record per tenant.
Resolving subdomains on Safari
Chrome, Firefox, and Edge resolve *.localhost to 127.0.0.1 automatically, but Safari does not. To make the tenant subdomains work in Safari during local development, map them in /etc/hosts:
-
Edit
/etc/hosts: -
Add an entry for each subdomain:
/etc/hosts -
Visit
http://siteA.localhost:3000,http://siteB.localhost:3000, and so on.
Example repository
For a complete working implementation, see the multi-tenant path example repository.