1
1 Comment

Using Caddy To Offer Subdomains To Your Users

If you are building an app where you want to offer your users their own subdomains i.e. john.yourapp.com, I will show you a way to do it using Caddy, which is a web server.

I am the creator of CourseLit which an open-source alternative to platforms like Teachable, Podia, Kajabi etc., and I have been using this exact setup for the last 2 years to host CourseLit. So the setup is battle tested.

Step 1: The setup

Let's talk about how things are laid out. If you have a domain called yourapp.com, this tutorial assumes you have the following setup.

  1. An app for the landing page and functionalities like new account creation, subscriptions etc., hosted at yourapp.com. Let's call it app1.
  2. A multi-tenant SaaS app, hosted at *.yourapp.com. Let's call it app2.

So, this setup assumes you have separate apps for handling the account creation (plus landing page) and the actual SaaS offering.

Both of these apps will be hosted in a single server (VPS) whose IP will be called IP.OF.YOUR.SERVER from here onwards.

Step 2: DNS Configuration

I highly recommend Cloudflare for DNS management. I assume that your domain is already on Cloudflare.

You need the following two A record in your DNS

A * IP.OF.YOUR.SERVER
A yourapp.com IP.OF.YOUR.SERVER

The first one will route traffic to your SaaS app and the second one will route traffic to your landing page (customer app).

The first one is also known as a wildcard record which means it will resolve any *.yourapp.com subdomain.

Step 3: Caddy setup

So Caddy is a web server which will sit in front of both of your apps and act as a reverse proxy to route traffic to app1 when the requests come in for yourapp.com and to app2 when the requests come in john.yourapp.com or any subdomain for that matter.

You need to install Caddy server on your VPS where both of the apps are going to be hosted. You can follow their official documentation for this.

Let's suppose your app1 is running on port 3000 and app2 is running on 3001.

Now you can configure Caddy to route traffic according to the rules described above using a Caddyfile which is a configuration file for Caddy.

In your Caddyfile, put in the following.

# New SSL certificates will be generated dynamically for a subdomain when it is requested for the first time. The following settings will be used to issue new certificates.
{
        email [email protected]
        on_demand_tls {
                # If you want to verify if a sub-domain is valid before getting a SSL certificate for it, you need to have an end-point in your app to return true/false.
                ask https://yourapp.com/api/domain/verify
        }
}

# Your apps will be behind HTTPS, hence we need this block.
:443 {
        tls {
                on_demand
        }

        reverse_proxy 3001

        encode gzip
}

*.yourapp.com {
        tls {
               # You need to obtain the API_TOKEN from Cloudflare dashboard. Follow the guide located at this link. https://github.com/caddy-dns/cloudflare
                dns cloudflare API_TOKEN
        }

        reverse_proxy 3001

        encode gzip
}

yourapp.com {
	reverse_proxy 3000
	encode gzip
}

Step 4: Bring up everything

Now you can start the Caddy server along with both the apps. If everything works, you should be able to navigate to your apps correctly.

That's it!

Conclusion

I hope some of my fellow indies will find this post useful in their future endeavours as it is a desired feature nowadays.

If you have more questions, please hit me up on Twitter at @rajatsx or come as in my Discord server.

on February 24, 2023
Trending on Indie Hackers
I spent $0 on marketing and got 1,200 website visitors - Here's my exact playbook User Avatar 74 comments Veo 3.1 vs Sora 2: AI Video Generation in 2025 🎬🤖 User Avatar 32 comments 🚀 Get Your Brand Featured on FaceSeek User Avatar 20 comments Solo SaaS Founders Don’t Need More Hours....They Need This User Avatar 19 comments Day 6 - Slow days as a solo founder User Avatar 16 comments Planning to raise User Avatar 12 comments