The shape of the problem
Many SaaS apps give each tenant their own subdomain: acme.yourapp.com, globex.yourapp.com, and so on. You can't manually create a DNS record and a TLS certificate every time someone signs up — you'd be doing ops work per customer. The solution is wildcard subdomains, which require three pieces working together:
- 1Wildcard DNS —
*.yourapp.comresolves to your app. - 2Wildcard TLS — a single certificate valid for
*.yourapp.com. - 3Tenant routing — your app reads the subdomain from the request and loads the right tenant.
Get all three right and a new tenant is live the instant they sign up, with valid HTTPS, no manual steps.
Step 1: Wildcard DNS
A wildcard DNS record matches any single label to the left of your domain. Add one record:
*.yourapp.com. 300 IN CNAME yourapp.com.Now acme.yourapp.com, globex.yourapp.com, and any other first-level subdomain all resolve to the same target. A few rules:
- A wildcard matches one label.
*.yourapp.commatchesacme.yourapp.combut notapp.acme.yourapp.com. For nested tenants you'd need*.*.yourapp.compatterns or per-level wildcards (and not all DNS providers support multi-level wildcards cleanly). - An explicit record always wins over the wildcard. If you have a real
www.yourapp.comrecord, it takes precedence — good, because you usually wantwww,api, andappto be non-tenant routes. - Keep the TTL modest (300s) while you're setting up so changes propagate fast.
Step 2: Wildcard TLS certificate
Every tenant subdomain needs valid HTTPS. You don't want to issue a cert per tenant — you want one wildcard certificate covering *.yourapp.com.
The catch: wildcard certificates from Let's Encrypt require the DNS-01 challenge, not HTTP-01. DNS-01 proves you control the domain by creating a TXT record, which is the only ACME challenge that can validate a wildcard.
# certbot wildcard via DNS-01 (Cloudflare plugin example)
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials ~/.secrets/cf.ini \
-d 'yourapp.com' -d '*.yourapp.com'Because DNS-01 needs API access to create TXT records, you typically automate it with your DNS provider's API token. The cert auto-renews every ~60 days. This is real operational surface area — token rotation, renewal monitoring, and propagation timing all matter.
Step 3: Tenant routing in your app
With DNS and TLS done, every tenant request arrives at your app. Now you extract the tenant from the Host header and scope the request:
// Express middleware
app.use((req, res, next) => {
const host = req.hostname; // acme.yourapp.com
const [sub] = host.split('.'); // acme
const reserved = ['www', 'app', 'api', 'admin'];
if (reserved.includes(sub) || host === 'yourapp.com') {
req.tenant = null; // marketing / non-tenant route
return next();
}
const tenant = lookupTenantBySlug(sub); // DB lookup, ideally cached
if (!tenant) return res.status(404).send('Unknown tenant');
req.tenant = tenant;
next();
});Then every query is scoped by req.tenant.id. This is the multi-tenant isolation boundary, and it must be enforced on every data access path — a missed WHERE tenant_id = ? is a cross-tenant data leak. Many teams enforce this at the database layer too, with PostgreSQL row-level security, so a forgotten filter fails closed.
Custom tenant domains (the next level)
Mature SaaS lets enterprise tenants bring their own domain — app.acmecorp.com instead of acme.yourapp.com. That's a different flow:
- 1Tenant adds a CNAME from their domain to yours.
- 2You issue a per-domain certificate (HTTP-01 works here since it's a specific hostname).
- 3Your routing maps the custom hostname to the tenant.
This is more work than wildcards but is table stakes for B2B. Plan your routing layer to handle both wildcard subdomains and explicit custom domains from day one.
How a platform handles the heavy parts
The DNS automation, ACME DNS-01 dance, and cert renewal are exactly the kind of toil a managed platform should absorb. On PandaStack, custom domains come with automatic SSL, DNS runs through Cloudflare, and ingress is handled by Kong — so you point your domain (including wildcards) at the app and the certificate lifecycle is managed for you. Your job shrinks to the part only you can do: the tenant routing and data isolation inside your application code.
Checklist and gotchas
| Piece | What to get right | Common failure |
|---|---|---|
| Wildcard DNS | *.yourapp.com record, modest TTL | Expecting it to match nested subdomains |
| Wildcard TLS | DNS-01 challenge, auto-renew | Trying HTTP-01 (won't issue wildcards) |
| Reserved subdomains | Exclude www/api/app/admin | A tenant named "api" hijacking a system route |
| Tenant routing | Scope every query by tenant | Missed filter → cross-tenant leak |
| Custom domains | Separate per-domain cert flow | Assuming wildcard covers customer domains |
Two final gotchas worth repeating: reserve your system subdomains *before* you open signups (otherwise a tenant grabs admin), and enforce tenant isolation at the data layer, not just in middleware. Wildcards make onboarding instant; disciplined routing keeps that convenience from becoming a security incident.
References
- [Let's Encrypt — Challenge types (DNS-01)](https://letsencrypt.org/docs/challenge-types/)
- [RFC 4592 — The role of wildcards in DNS](https://datatracker.ietf.org/doc/html/rfc4592)
- [MDN — Host request header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host)
- [PostgreSQL — Row Security Policies](https://www.postgresql.org/docs/current/ddl-rowsecurity.html)
Building multi-tenant SaaS? PandaStack gives you custom domains with automatic SSL out of the box — start on the free tier: [dashboard.pandastack.io](https://dashboard.pandastack.io)