Back to Blog
Guide6 min read2026-05-01

Subdomain Routing: How to Route Traffic by Subdomain

Learn how to configure subdomain-based routing for multi-tenant SaaS apps, staging environments, and microservice architectures.

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 → Production
  • staging.example.com → Staging
  • dev.example.com → Development

Service separation:

  • app.example.com → Frontend
  • api.example.com → Backend API
  • docs.example.com → Documentation site

Multi-tenant SaaS:

  • acme.example.com → Acme Corp's workspace
  • globex.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:   3600

For dynamic multi-tenant subdomains (where any subdomain should resolve), use a wildcard record:

Type:  A
Name:  *
Value: 203.0.113.10
TTL:   3600

Now 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.

Ready to deploy?

Start free on PandaStack — no credit card required.

Start free on PandaStack

More in Guide

Browse all Guide articles →