Subdomain Routing: How to Route Traffic by Subdomain
Subdomain routing lets you use different subdomains to serve different content, environments, or tenants — all from a single domain. api.example.com, staging.example.com, and tenant.example.com can each point to different services. This guide covers DNS setup, web server configuration, and application-level subdomain routing.
Common Subdomain Routing Patterns
Environment separation:
app.example.com→ Productionstaging.example.com→ Stagingdev.example.com→ Development
Service separation:
app.example.com→ Frontendapi.example.com→ Backend APIdocs.example.com→ Documentation site
Multi-tenant SaaS:
acme.example.com→ Acme Corp's workspaceglobex.example.com→ Globex's workspace
Step 1: DNS Configuration
You need DNS records for each subdomain. For a few static subdomains, add individual records:
Type: CNAME
Name: api
Value: your-api-server.pandastack.io
TTL: 3600
Type: CNAME
Name: staging
Value: your-staging-server.pandastack.io
TTL: 3600For dynamic multi-tenant subdomains (where any subdomain should resolve), use a wildcard record:
Type: A
Name: *
Value: 203.0.113.10
TTL: 3600Now anything.example.com resolves to your server's IP. Your application reads the Host header to determine which tenant to serve.
Step 2: Nginx Subdomain Routing
Route specific subdomains to different backends
# api.example.com → API server on port 8080
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
# app.example.com → Frontend on port 3000
server {
listen 443 ssl http2;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
}
}Wildcard subdomain routing (multi-tenant)
server {
listen 443 ssl http2;
server_name ~^(?<tenant>[^.]+).example.com$;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Tenant $tenant;
}
}The regex captures the tenant name and passes it as a header.
Step 3: Application-Level Subdomain Detection
Your app reads the Host header to identify which tenant or environment is being served.
Node.js / Express
app.use((req, res, next) => {
const host = req.hostname; // e.g., "acme.example.com"
const parts = host.split('.');
if (parts.length >= 3) {
req.tenant = parts[0]; // "acme"
}
next();
});
app.get('/dashboard', async (req, res) => {
const { tenant } = req;
if (!tenant) return res.status(400).json({ error: 'No tenant' });
const org = await Organization.findOne({ where: { slug: tenant } });
if (!org) return res.status(404).json({ error: 'Tenant not found' });
res.json({ org });
});React Frontend
// Detect tenant from hostname on page load
const getTenant = () => {
const hostname = window.location.hostname; // "acme.example.com"
const parts = hostname.split('.');
return parts.length >= 3 ? parts[0] : null;
};
const tenant = getTenant();Wildcard SSL Certificates
For wildcard subdomains, you need a wildcard TLS certificate: *.example.com. Let's Encrypt supports wildcard certs via the DNS-01 challenge:
certbot certonly --manual --preferred-challenges dns -d "*.example.com" -d "example.com"You'll be prompted to add a _acme-challenge TXT record to your DNS. Certbot then verifies it and issues the wildcard cert. Certbot plugins for Cloudflare, Route 53, and others can automate this.
Traefik: Dynamic Subdomain Routing with Docker
If you're running Docker containers, Traefik can dynamically route based on subdomain labels:
version: '3'
services:
frontend:
image: frontend:latest
labels:
- "traefik.http.routers.frontend.rule=Host(`app.example.com`)"
api:
image: api:latest
labels:
- "traefik.http.routers.api.rule=Host(`api.example.com`)"
staging:
image: frontend:staging
labels:
- "traefik.http.routers.staging.rule=Host(`staging.example.com`)"Each container is automatically routed based on its subdomain label. No Nginx config needed.
Subdomain Routing on PandaStack
PandaStack supports custom domains for all deployment types — static sites, Docker containers, managed WordPress, and more. You can point any subdomain to a specific PandaStack project by adding a CNAME record at your registrar and configuring the domain in the dashboard at [dashboard.pandastack.io](https://dashboard.pandastack.io). TLS certificates are issued automatically via Let's Encrypt. See [docs.pandastack.io](https://docs.pandastack.io) for step-by-step instructions.
Conclusion
Subdomain routing is a powerful pattern for multi-tenant SaaS, environment isolation, and service separation. Set up wildcard DNS, configure your reverse proxy to capture the subdomain and pass it to your app, and use wildcard TLS certificates for blanket HTTPS coverage. With the right infrastructure in place, adding a new tenant or service is as simple as adding a DNS record.