Scaling SaaS Multi-Tenancy: Implementing On-Demand TLS with Caddy Server v2
Introduction to the Multi-Tenant Domain Challenge
In the modern Software-as-a-Service (SaaS) landscape, offering users the ability to connect their own custom domains (e.g., userbrand.com instead of user.saasplatform.com) is no longer a luxury feature—it is a core business requirement. Whether you are building an e-commerce platform, a website builder, or a blogging network, providing a white-label experience is vital for customer retention and branding.
However, managing SSL/TLS certificates for thousands of external, dynamic domains presents a massive technical hurdle. Traditional approaches involving Let's Encrypt with custom cron jobs, shell scripts, or complex reverse proxy configurations frequently run into rate limits, synchronization issues, and heavy operational overhead. This is where Caddy Server v2 changes the game. With its native support for On-Demand TLS, Caddy automates the entire lifecycle of SSL certificates on the fly, making multi-tenant custom domain management incredibly efficient and resilient.
Understanding Caddy Server v2 and On-Demand TLS
Caddy Server v2 is an open-source, extensible web server written in Go, famous for being the first web server to enable automatic HTTPS by default. While standard automated HTTPS requires you to hardcode hostnames in a configuration file, On-Demand TLS allows Caddy to obtain a certificate during the standard TLS handshake process when an unconfigured domain hits the server for the very first time.
How On-Demand TLS Transforms the Handshake
When a visitor navigates to a customer's custom domain pointing to your SaaS infrastructure, the following sequential process occurs:
- The browser initiates a TLS handshake with your Caddy reverse proxy.
- Caddy intercepts the Server Name Indication (SNI) extension to identify the requested domain.
- If Caddy does not possess a valid certificate for this domain, it triggers an internal check to verify if the domain is authorized to use your service.
- Upon successful validation, Caddy communicates with Let's Encrypt or ZeroSSL to request, validate, and install an SSL certificate in real-time.
- The TLS handshake completes successfully, and the user is securely connected via HTTPS, all within a matter of seconds.
Critical Security Warning: Enabling On-Demand TLS without proper guardrails opens your infrastructure to Denial of Service (DoS) attacks. An attacker could point thousands of random domains to your server's IP address, forcing Caddy to exhaust your ACME provider's rate limits and deplete your server storage. Therefore, a robust authorization endpoint is absolutely mandatory.
Architecture for SaaS Multi-Tenant Custom Domains
To implement this system securely and reliably, your infrastructure requires three primary components working in harmony:
- DNS Configuration: Your tenants must create a
CNAMErecord pointing their custom domain to your platform's primary routing domain (e.g.,connect.yoursaas.com), or anArecord pointing directly to your Caddy server's public IP address. - Caddy Server Edge Proxy: Acts as the entry point for all traffic, managing TLS generation and reverse-proxying requests to your backend application servers.
- Backend Validation API: A lightweight internal endpoint within your core SaaS application that Caddy queries to verify if an incoming custom domain belongs to an active, paying customer.
Step-by-Step Implementation Guide
Step 1: Preparing Your Backend Validation Endpoint
Before configuring Caddy, you must expose an internal or external HTTP endpoint that returns a 200 OK status code if a domain is permitted to receive a certificate, or a 400/404 status code if it is rejected. Caddy passes the domain as a query parameter named domain.
Here is a conceptual example of a backend controller handling this request:
// Example in pseudocode / Node.js
app.get('/api/v1/check-domain', async (req, res) => {
const targetDomain = req.query.domain;
const isRegistered = await database.lookupDomain(targetDomain);
if (isRegistered && isRegistered.isActive) {
return res.status(200).send('Authorized');
}
return res.status(404).send('Unauthorized');
});Step 2: Designing the Caddyfile
The Caddy configuration file (Caddyfile) must be structured to handle global TLS options and define the reverse proxy behavior. Below is a production-ready Caddyfile template designed specifically for multi-tenant SaaS environments:
{
# Global configuration options
email [email protected]
# Configure On-Demand TLS global options
tls {
on_demand {
ask http://localhost:8080/api/v1/check-domain
interval 2m
burst 5
}
}
}
# Catch-all block for all HTTP traffic to enforce HTTPS redirect
http:// {
redir https://{host}{uri}
}
# Catch-all block for handling all HTTPS multi-tenant domains
https:// {
tls {
on_demand
}
# Reverse proxy traffic to your internal backend application
reverse_proxy [http://127.0.0.1:3000](http://127.0.0.1:3000) {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}Step 3: Analyzing Key Caddyfile Directives
To ensure optimal platform stability, it is crucial to understand the configurations utilized above:
ask: This directive specifies the endpoint Caddy calls via an HTTP GET request to check permission before issuing a certificate. If this is omitted, Caddy will issue certificates for any domain, creating a severe vulnerability.intervalandburst: Rate-limiting parameters that prevent abusive certificate issuance spikes, keeping your infrastructure well within ACME provider limits.header_up Host {host}: Instructs Caddy to forward the original custom domain name to your application backend, allowing your application code to determine which tenant's data to display.
Best Practices for Production Deployment
1. High Availability and Distributed Storage
By default, Caddy saves certificates to the local file system. In a clustered or multi-instance setup behind a cloud load balancer, this creates a state problem. To solve this, you should configure Caddy to use a distributed storage module, such as Redis, Amazon S3, or a shared database, ensuring all Caddy instances share the same certificate pool seamlessly.
2. Proactive Monitoring and Alerting
Always monitor the response latency of your ask endpoint. If your database experiences downtime and the validation endpoint fails to respond quickly, Caddy will deny incoming TLS handshakes, resulting in downtime for your customers' sites. Implement caching mechanisms (like Redis) on your validation endpoint to maintain sub-millisecond response times.
3. Pre-flight DNS Verification
To improve user experience and avoid unnecessary resource consumption on your proxy, implement a "Verify Connection" button within your SaaS customer dashboard. Before allowing users to route traffic through your platform, run a backend check to ensure their CNAME or A records are successfully pointing to your servers.
Conclusion
Implementing custom multi-tenant domains no longer requires dedicating dozens of engineering hours to maintain complex, brittle reverse proxy scripts. Caddy Server v2 with On-Demand TLS offers a production-grade, secure, and incredibly elegant solution that scales effortlessly alongside your business. By combining Caddy's automated certificate lifecycle with a robust backend validation strategy, you can confidently offer a fully branded, secure white-label experience to your SaaS customers.
