Configure Wildcard DNS in Namecheap for Multi-Tenant Apps

by Fahim

Calling registrar APIs during user signup just to provision a subdomain is a recipe for broken onboarding, rate limits, and maintenance headaches. If your SaaS hands out tenant workspaces like acme.yourdomain.com or team-alpha.yourdomain.com, you need dynamic routing that works instantly without touching DNS tables every time someone registers.

Wildcard DNS fixes this cleanly. It catches every unregistered subdomain and routes it straight to your server IP or load balancer. I set this up across Namecheap, an Nginx reverse proxy, and a Node.js multi-tenant backend on Ubuntu 22.04 LTS. Here is how to wire it up properly while dodging the usual headaches with SSL issuance and DNS precedence.

Terminal screen showing DNS query and Nginx wildcard configuration for multi-tenant web application
Terminal screen showing DNS query and Nginx wildcard configuration for multi-tenant web application

How Wildcard DNS Works (and How Namecheap Handles It)

A wildcard DNS record uses an asterisk (*) as the host prefix. When a DNS resolver queries your domain for a hostname that lacks an explicit, hardcoded record, the nameserver falls back to this wildcard rule.

Per RFC 4592, wildcards only synthesize answers when no closer match exists. If you have an explicit A record for app.yourdomain.com pointing to Server A, and a wildcard record *.yourdomain.com pointing to Server B, traffic for app.yourdomain.com goes to Server A. Any random query like test-company.yourdomain.com drops into Server B.

Namecheap supports wildcard entries in both Namecheap BasicDNS and Namecheap PremiumDNS. You can stick wildcards on A records, AAAA records, and CNAME records. If your root domain already points to a marketing site on Vercel or Netlify, you can safely route the wildcard subdomain record to your app server without breaking your root homepage.

Step 1: Configure the Wildcard Record in Namecheap BasicDNS

Log in to Namecheap, head to your Domain List, and click Manage next to your domain. Open the Advanced DNS tab.

First, double-check your Nameservers setting under the main Domain tab. It has to be set to Namecheap BasicDNS or Namecheap Web Hosting DNS. If your nameservers point elsewhere (like Cloudflare or AWS Route 53), changing records inside Namecheap won’t do a thing.

Under the Host Records table in Advanced DNS, click Add New Record.

Pick one of these two setups depending on your infrastructure:

  • Option A: Single VPS or Dedicated Server (A Record)
    • Type: A Record
    • Host: *
    • Value: 198.51.100.42 (your server’s public IPv4)
    • TTL: Automatic (or 5 min if you plan to migrate soon)
  • Option B: Load Balancer or PaaS Endpoint (CNAME Record)
    • Type: CNAME Record
    • Host: *
    • Value: lb-origin.yourdomain.com. (add the trailing dot if your UI prompts for it)
    • TTL: Automatic

Hit the green checkmark to save. If you need your root apex domain to point to an external hostname without clashing with the wildcard, read our guide on how to point a Namecheap apex domain to a hostname target using ALIAS records.

Don’t enter *.* in the host field. Wildcard DNS standard specs only match a single label. A record on * matches tenant.yourdomain.com, but it will never match api.tenant.yourdomain.com.

Step 2: Verify DNS Resolution with Dig

Namecheap usually pushes changes to its authoritative servers within a few minutes, but your local ISP or OS cache might take longer. Skip testing in a browser for now—use dig directly from your terminal.

Query an arbitrary, non-existent subdomain to make sure the wildcard answers:

dig +short random-tenant-test-8492.yourdomain.com @8.8.8.8

You should get your server IP (like 198.51.100.42) in the ANSWER section. If the query hangs or returns empty, check Namecheap’s authoritative servers directly (you can grab their hostnames from your dashboard):

dig +short *.yourdomain.com @dns1.registrar-servers.com

If Namecheap’s nameservers answer with your IP, your DNS setup is solid. Any issues on your local box are just downstream resolver caching.

Step 3: Route Dynamic Subdomains in Nginx

Now that Namecheap routes *.yourdomain.com to your server, Nginx has to accept arbitrary hostnames. By default, Nginx dumps unlisted hostnames into your default_server block or flat out rejects them.

Open your virtual host config (usually in /etc/nginx/sites-available/yourdomain.conf) and configure a server block for the wildcard. We can parse the tenant slug right in Nginx and forward it downstream as a header:

server { listen 80; listen [::]:80; server_name ~^(?[a-z0-9-]+).yourdomain.com$; # Disallow internal or system reserved subdomains if ($subdomain ~* ^(admin|api|mail|billing|static)$) { return 404; } location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Tenant-Slug $subdomain; proxy_cache_bypass $http_upgrade; }
}

The regex matches the tenant slug (like alpha from alpha.yourdomain.com), stores it in $subdomain, and passes it along as X-Tenant-Slug. If your stack relies on real-time sockets through this proxy, check our guide on how to configure an Nginx reverse proxy for Node.js WebSockets and PM2.

Verify your syntax and reload Nginx:

sudo nginx -t && sudo systemctl reload nginx

Step 4: Generate a Wildcard SSL Certificate with Let’s Encrypt

This is where most setups hit a snag. Standard Let’s Encrypt certificates run through an HTTP-01 challenge, where Certbot drops a token into .well-known/acme-challenge/ on your web server and Let’s Encrypt pings it over HTTP.

As outlined in the Let’s Encrypt Challenge Documentation, wildcard certs (*.yourdomain.com) mandate the DNS-01 challenge. You cannot validate a wildcard cert over plain HTTP.

For DNS-01 validation, Certbot requires you to publish a temporary _acme-challenge.yourdomain.com TXT record in Namecheap to verify domain ownership.

Running the Manual DNS Challenge First

Before scripting everything, issue the cert manually to verify your records work:

sudo certbot certonly  --manual  --preferred-challenges=dns  --email admin@yourdomain.com  --server https://acme-v02.api.letsencrypt.org/directory  --agree-tos  -d yourdomain.com  -d "*.yourdomain.com"

Certbot spits out a verification string and halts:

Please deploy a DNS TXT record under the name:
_acme-challenge.yourdomain.com
with the following value:
5xL9_SampleACMETokenStringForValidation42w8

Head back to the Namecheap Advanced DNS tab:

  1. Click Add New Record.
  2. Choose TXT Record.
  3. In Host, type _acme-challenge (don’t add your domain name; Namecheap appends it for you).
  4. In Value, paste the hash Certbot provided.
  5. Set TTL to 1 min.
  6. Click the green checkmark to save.

Don’t hit Enter in Certbot just yet. Open a second terminal window and poll the record with dig until it resolves:

dig +short TXT _acme-challenge.yourdomain.com @8.8.8.8

Once the token shows up in the dig output, hop back to Certbot and hit Enter. Certbot will verify the TXT record, issue the certificate, and drop your keys into /etc/letsencrypt/live/yourdomain.com/fullchain.pem.

If you have CAA records set up, make sure they aren’t blocking Let’s Encrypt. Here’s our tutorial on how to configure CAA records in Namecheap DNS to restrict SSL issuance.

Automating DNS-01 Renewals with Namecheap API

Manual TXT records fall apart when your cert hits its 90-day renewal deadline. To automate this, you can use the Certbot Namecheap plugin (certbot-dns-namecheap). Note that Namecheap requires either a $50 account balance or 20 domains on file before they enable API access.

If your account qualifies, turn on API access in your Namecheap profile settings and whitelist your server’s public IP. Then install the plugin:

sudo apt-get install python3-pip -y
sudo pip3 install certbot-dns-namecheap

Save your credentials to /etc/letsencrypt/namecheap.ini:

dns_namecheap_username = your_namecheap_username
dns_namecheap_api_key = 4a123bc456def789ghi012jkl345mno6

Lock down the file permissions so other system users can’t read your API key:

sudo chmod 600 /etc/letsencrypt/namecheap.ini

Issue or renew certificates unattended with this command:

sudo certbot certonly  --authenticator dns-namecheap  --dns-namecheap-credentials /etc/letsencrypt/namecheap.ini  --dns-namecheap-propagation-seconds 60  -d yourdomain.com  -d "*.yourdomain.com"

If your account doesn’t qualify for Namecheap API access, look into acme-dns for CNAME-based challenge delegation, or offload your nameservers to Cloudflare while keeping your registration at Namecheap.

Step 5: Bind the Certificate to Nginx

Now update your Nginx server block to terminate TLS using your new wildcard certificate:

server { listen 80; listen [::]:80; server_name yourdomain.com *.yourdomain.com; return 301 https://$host$request_uri;
} server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name ~^(?[a-z0-9-]+).yourdomain.com$; ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Tenant-Slug $subdomain; }
}

Reload Nginx: sudo systemctl reload nginx. Load https://test-tenant.yourdomain.com in your browser. You should see a valid HTTPS lock with the certificate issued for *.yourdomain.com.

Step 6: Handle Dynamic Tenants in Node.js

Once requests hit port 3000, your app needs to inspect the tenant slug, fetch their database records, and isolate user state. Here’s an Express.js middleware using the X-Tenant-Slug header we set in Nginx:

import express from 'express'; const app = express(); // In-memory tenant mock store (use your DB in production)
const tenants = new Map([ ['alpha', { id: 't_01', name: 'Alpha Corp', plan: 'enterprise' }], ['bravo', { id: 't_02', name: 'Bravo Studio', plan: 'pro' }]
]); function tenantResolver(req, res, next) { // Read the custom header from Nginx first, fall back to parsing Host let slug = req.headers['x-tenant-slug']; if (!slug && req.headers.host) { const parts = req.headers.host.split('.'); if (parts.length > 2) { slug = parts[0].toLowerCase(); } } if (!slug) { return res.status(400).json({ error: 'Missing tenant identifier.' }); } const tenant = tenants.get(slug); if (!tenant) { return res.status(404).json({ error: `Tenant '${slug}' not found.` }); } // Attach tenant record to the request lifecycle req.tenant = tenant; next();
} app.use(tenantResolver); app.get('/api/dashboard', (req, res) => { res.json({ message: `Welcome to ${req.tenant.name}`, tenantId: req.tenant.id, plan: req.tenant.plan });
}); app.listen(3000, () => { console.log('App running on port 3000');
});

If you run your app via PM2 on a VPS, take a look at our guide on how to deploy Next.js on a VPS with PM2, Nginx, and Namecheap DNS for a reliable zero-downtime setup.

Gotchas with DNS Precedence and Collisions

Here are a few routing quirks that trip people up with wildcard setups.

1. Explicit Records Always Win

If you add an explicit A record for mail or crm in Namecheap, that record overrides the * record completely. This is by design. You can spin up landing pages or marketing tools on specific subdomains without worrying about the wildcard pulling traffic away.

2. Multi-Level Wildcards Do Not Cascade

Querying sub.tenant.yourdomain.com won’t match *.yourdomain.com. DNS rules state an asterisk covers only a single label between dots. If you need nested routing like staging.acme.yourdomain.com, you’ll need an explicit sub-wildcard record in Namecheap: set Host to *.acme pointing to your IP.

3. The Apex Domain (@) Is Not Covered

A wildcard * does not match the bare root domain yourdomain.com. You still need an explicit A, ALIAS, or CNAME record on host @ in Namecheap. If you want the apex domain to hit your app as well, set both @ and * to your server IP.

Frequently Asked Questions

Does Namecheap charge extra for wildcard DNS records?

No. Wildcard records work on all standard domains using free Namecheap BasicDNS. You don’t need PremiumDNS just to set an asterisk record.

Can I use a wildcard CNAME pointing to an external platform?

Yes. If your backend runs on Heroku, Render, or an AWS ALB, set a CNAME record with Host set to * and Value set to your platform’s canonical domain. Just make sure the target platform supports dynamic wildcard SSL bindings on their end.

How long does Namecheap take to propagate wildcard DNS changes?

Namecheap’s authoritative servers update within 3 to 5 minutes. However, local recursive resolvers might cache old NXDOMAIN responses based on your SOA record’s negative caching TTL, which can take anywhere from 15 minutes to an hour.

Do wildcard SSL certificates cover sub-subdomains?

No. A certificate issued for *.yourdomain.com is strictly valid for single-level subdomains like tenant.yourdomain.com. It triggers a browser SSL warning on nested hostnames like app.tenant.yourdomain.com.

Next Steps

With wildcard DNS running in Namecheap and reverse proxying configured in Nginx, new tenant signups work out of the box with zero registrar API calls. From here, you’ll likely want to let customers bring custom domains (like workspace.clientcorp.com). Check out how to handle proxy routing for custom hostnames in our Namecheap ALIAS and CNAME configuration tutorial.

all_in_one_marketing_tool