I am trying to setup a multi-tenant application with Caddy server. The application works perfectly fine on my local setup with Laravel valet; allowing me to create subdomains with https. The following configuration works on production, allowing me to create subdomains.
However, the system fails when I try to map a subdomain from client to a subdomain on my application.
I am unable to figure out if there's an error in my application OR the way I am configuring DNS.
I am replicating the entire setup on my test-domain: layoff.wtf
My Caddyfile Configuration:
{
on_demand_tls {
ask http://layoff.wtf/caddy/ask
interval 2m
burst 5
}
}
https:// {
tls {
on_demand
}
root * /home/forge/layoff.wtf/public
file_server
php_fastcgi unix//run/php/php8.2-fpm.sock
}
This setup works perfectly to serve:
- My main domain:
layoff.wtf
with HTTPS - Any subdomain:
<subdomain>.layoff.wtf
with HTTPS
Problem
My customer has created following subdomain on my SaaS: waitlist.layoff.wtf
They want to serve it via their subdomain: support.waitlist.guru
Here's how the DNS has been configured for support.waitlist.guru
:
CNAME | support | waitlist.layoff.wtf. | 600 seconds
That way, I thought when the user types support.waitlist.guru
, they will be served my SaaS application from waitlist.layoff.wtf
.
However, they are being served the homepage on layoff.wtf
and not the appropriate subdomain. You can actually type those names in browser and check.
DNS configuration for my SaaS domain: layoff.wtf
is as follows:-
A | @ | 13.233.62.52 | 600 seconds
CNAME | * | layoff.wtf. | 600 seconds
How do I ensure that my customers can create their subdomains and map to their domain using a simple CNAME configuration; which I have seen on multiple SaaS offerings?
Update
After multiple attempts at fixing this; I think the issue is with the way my Laravel code handles the mapped domain. Here's what I found:
Route::middleware('appendSubdomainInfo')->domain('{subdomain_slug}' . '.'. config('app.primary_domain'))->group(function () {
Route::middleware('subdomainAccessChecker')->group(function() {
// All my subdomain routes go here.
Route::get('/check', function($subdomain_slug) {
dd($subdomain_slug);
});
});
});
The above code throws correct output subdomain when accessed on subdomain.my.app/check; but throws 404 error when run on subdomain.customer.app/check
In summary; Laravel does receive the request; but fails to deliver correct output for a mapped domain. Works fine for a regular subdomain.
In a multi-tenant SaaS application, when you want a customer's custom subdomain to resolve to a tenant-specific subdomain on your domain, you must consider about both on the DNS level within your application and web server configuration. If your customer has set up a
CNAME
record forsupport.waitlist.guru
pointing towaitlist.layoff.wtf
, then the DNS should resolvesupport.waitlist.guru
to your server's IP address.Caddy is configured to handle requests for any domain, not just subdomains of
layoff.wtf
:In this setup, Caddy uses a wildcard matcher for hostnames (..layoff.wtf support.*.guru) to match any subdomain of
layoff.wtf
andsupport.*.guru
. You'll need to adjust the matchers based on your needs.