21
46 Comments

How to implement support for custom domains in a SaaS product?

I'm working on a SaaS product where currently we provide a sub-domain for our users. Currently we redirect a wild card subdomain to a specific folder in our database and serve through NGINX reverse proxy.

But we would like to offer them a custom domain option so that they can have their own custom domain instead of our subdomain url.

I have seen many SaaS services does this but unable to figure out how its done. Any pointers on it will be of great help!

Update : Please consider achieving the above dynamically runtime. It would be great if you can share your workflow.

posted to Icon for group Developers
Developers
on January 29, 2021
  1. 5

    We do this at Nhost using Caddy instead of Nginx.

    You only need to make sure the custom domain points towards the right server with caddy, configure Caddy with the custom domain and Caddy will manage the certificates for you.

    1. 1

      Is it possible to add the custom domain configuration through API's? I want to automate the process when a new customer links their domain.

    2. 1

      I have gone through Caddy sometime back but thanks for pointing me to explore on it again!

  2. 3

    You should tell users to use CNAME and not an A record when pointing to your app in case you change IPs in the future. They could probably set their domain CNAME record to the wildcard subdomain.

    Think of it as
    customer domain -> customer.yourapp.com

    DNS on customer side would be:
    www.customersite.com CNAME customer.yourapp.com

    The problem is if they want to use both www & non-www. If they always use something specific as a subdomain, probably not an issue.

    You would then need to code the hostname logic in your app.

    1. 2

      Thanks for the steps. If its just a CNAME record by the user, I was wondering why many SaaS products offer it as a premium model? Anything thats need to be setup on development end?

    2. 1

      So if the customer wants to use their custom domain with non-www, they cannot do that with CNAME?

      If they can, what kind of custom logic we would need on our server/app?

      (I am facing the same problem right now, I was thinking to do with the A record pointing to my IP but what if the IP changes, I cannot ask every customer to change the IP again and theres dns propagation time as well which will bring a downtime for each customer's business)

  3. 2

    Lots of good answers in this thread, and I'd recommend Caddy for anyone who wants to build and manage this themselves. It's API and SSL cert management are fantastic.

    For anyone looking to pay for a service to handle this instead, I've built approximated.app to make this as easy and reliable as possible. It's features include:

    • It's automatable with an easy API, adding a custom domain/subdomain is one API call
    • You get your own dedicated Anycast IP address that you can give your users so that they can point apex domains (no www/subdomain)
    • If your app uses a CDN or has globally distributed servers, you can add global edge server regions so that the request always has the lowest possible latency.

    Anyone can register now and get a free 30 day trial to see if it suits your needs.
    After that it's $15/month per region (many apps only need one).

  4. 2

    Did you tried https://approximated.app/ This looks ideal for your use cases, we are evaluating this

    1. 1

      Yea, I recently came across this. But seems like its not yet public.

      1. 1

        Request for access and you will get it with in a day. Yesterday i requested and today i got their email.

        1. 1

          Thanks Sreekanth, as of today public registrations are now available too!

  5. 2

    I previously got this working using nginx. I had a quick look through my notes but can’t find the exact config at the minute. So what follows is from memory.

    I had an nginx load balancer, that terminated SSL connections using letsencrypt certs, and forwarded the connection to the an application server, re-encrypting the connection using local certs, the application servers each had an nginx installed running as a reverse proxy to direct the connection to the correct port.

    The tricky part was getting the SSL certs to work for each custom domain. I found there was a config on the load balancer nginx possible where you can use the hostname in the path to the ssl cert I.e. the hostname is available in a variable and you use that in the ssl cert path. That makes it dynamic, so 1 config works for all custom domains. Just add the ssl certs you create to a sensible place in a hostname folder.

    I’ll see later if I can locate the exact config, but hopefully this gives you a vague idea of how to get it working with nginx.

    1. 1

      Update - I found the nginx load balancer config. When setting the ssl_certificate and ssl_certificate_key inside the server block use the $ssl_server_name variable, that way you can terminate the SSL connection against the right certificate. Just make sure when you generate the certificate you put it in a folder named using the domain name. If you use certbot to generate the certs that happens by default.

      Here’s an example of that taken from my working config:

      ssl_certificate /etc/ssl/webapps/letsencrypt/$ssl_server_name/fullchain.pem;
      ssl_certificate_key /etc/ssl/webapps/letsencrypt/$ssl_server_name/privkey.pem;
      
      1. 1

        Hi @mjgs thanks for sharing. We're currently facing a problem with custom domains. So we were able to apply SSL via proxy but now the customer's site now exists on 2 domains at the same (the new custom domain and the old subdomain of our app). Any ideas how we can fix this to only allow the custom domain to show the site contents?

        1. 2

          Not off the top of my head, that project was over a year ago and isn’t fresh in my mind.

          1. 1

            No worries, I appreciate the response @mjgs !

  6. 2

    Not sure if the other answers have helped already, but having had to solve this stuff myself in the recent past, I sense your problems are probably about how to 1) add custom domains dynamically to the web server, 2) issue TLS certificates. Is this correct?

    If yes, the way you do it depends on the way you handle web apps generally speaking.

    For example I run https://www.dynablogger.com in Kubernetes, so whenever a user adds a custom domain to one of their blogs, my app creates an "ingress resource" via the Kubernetes API. The ingress resource is just a virtual host in the Nginx ingress controller.

    The ingress resource for the custom domain points to my app of course, and once it is active, a background job waits until a specific request to a URL with the custom domain returns a special token. When that happens, the app knows that the domain is actually pointing to the app and "upgrades" the ingress resource with some annotations for cert-manager, which will then issue the TLS certificate with Let's Encrypt. cert-manager/Let's Encrypt also do a verification of the domain, but because there are some rate limits on Let's Encrypt side, I prefer doing a verification before hand.

    It works pretty well.

    In a more traditional, non-Kubernetes environment, the easiest solution I have found and used in the past is with OpenResty, a version of Nginx with Lua scripting integrated. You can use some Lua modules to both verify that the domain points to the app (by using an API call for example that triggers a database lookup or something), and issue the TLS certificate with the very first https request for the custom domain.

    These are the two options I have used with success myself.

    1. 1

      Using bunny..net as reverse proxy and DNS zone manager is one of the easiest way that we found.

    2. 1

      Thanks for pointing at OpenResty. I think its time to look into it. If I find a way to automate the configuration for each customer, that would be great.

      1. 1

        There isn't much that you need to do.

        1. Create a single virtual host that accepts any hostnames
        2. Configure lua-ssl to make an API request to an endpoint of your app asking if the domain is allowed
        3. At the first request for that domain the TLS cert will be issued.

        There are many examples online of configuration with lua-ssl etc.

  7. 2

    I'm not sure how others approach this but you could support custom domains dynamically by using the Kong Gateway https://konghq.com/kong/

    It is basically a nginx which exposes APIs for its configuration. It allows to use all kinds of plugins which are also configured via API (Letsencrypt, caching, token-auth, rate-limiting, req/response transformations, analytics, logging, etc).

    You can instruct your customers, that already have a domain registered, to point their it to your kong instance. You can then tell kong to proxy all requests with the domain as host to your upstream service.

    In your upstream server you then have to make sure that you reply with the contents that belong to that domain (instead of your wildcard subdomain).

    I'm currently building such an integration. I'm also looking into ways of integrating a domain registrars APIs for directly registering and autoconfiguring domains within the app.

    Let me know if you have any specific questions :)

    1. 1

      Thanks for sharing. If you find a workflow for automating the whole configuration, please let me know.

  8. 2

    I address this in upcoming product: https://gumroad.com/l/howtocodenow.

    You just have to add the following nginx file:

    server {
    server_name CUSTOMDOMAIN.TLD;

    root /var/www/CUSTOMDOMAIN.TLD;
    index index.html;
    
    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
    }
    
    error_page 404 /404.html;
    

    }
    ---------------------------------------------------------------------------------------------------
    change CUSTOMDOMAIN.TLD to the name of the custom domain you want to support.

    and then do the following command:

    nginx -s reload
    ----------------------------------------------------------------------------------------------------

    Finally you have to instruct the domain owner to add an A record that points to your servers IP address at their domain registrar.

    Buy my product if you want to learn how to add free SSL to the custom domain or if you want to serve an app instead of a static page.

    1. 1

      @fastur Won't just the CNAME record does the job?

      1. 1

        Nothing will work unless you buy the product

  9. 2

    Another option for this is using something like Fastly: https://docs.fastly.com/en/guides/working-with-domains

    You can create subdomains using their API. Fastly is actually the CDN service a lot of massive companies use (Github, Firebase, etc.)

    1. 1

      Is there a possibility to automate the linking of custom domain to subdomains?

      1. 1

        I'm no DNS expert, but I think you can point a CNAME to your created domains: https://docs.fastly.com/en/guides/adding-cname-records

  10. 1

    hello ayyappa, i have created a saas product. i am also looking for same functionality. can we connect ?

  11. 1

    Anybody looking for an open source solution?
    Here is one we built: sireto/custom-domain
    Available on GitHub with Apache2 Licence.

    1. 1

      hello i am intrested in your solution. sent you a message to your email . please check your email. thank you so much .

      1. 1

        Hi @anumyam! how can I help you? You can create a Github issue where you need help at the repo mentioned above.

  12. 1

    I made https://appmasker.com to make custom domains simple and automated. We also do Caddy server orchestration for more bespoke implementations.

    1. 1

      Hi Cory, I am having a lot of issues with mysass.com/home?vendor=vendorid
      For simple like domain or subdomain it works fine. for the case I said neither mysass.com/home?vendor=vendorid:443 or mysass.com:443/home?vendor=vendorid works. Can you please help here ?

  13. 1

    I was able to set up custom domains (aka vanity domains) for my SaaS and I want to share how I did it.

    My app lets you generate both subdomains such as MYNAME.saas.com, or use your own domain such as MYNAME.com.

    Here's how I did it:

    • I set up an Ubuntu server and installed both Apache and Nginx. Nginx works a reverse proxy that receives ALL requests and forwards them to Apache.
    • Apache is the "real web server", and I call it real only because it's the one that processes the actual request.
    • The "challenge" is to generate SSL certificates for each custom domain your customers create. But as someone pointed out in a previous comment, Cloudflare support SSL for SaaS for FREE (up to 100 domains - any extra domains will have a $0.10/mo fee). This means that I'm able to generate certificates for any domain my customers decide to use.
    • I create/remove domains on Cloudflare on-demand using their Rest API.
    • It works beautifully.

    If anyone needs the nginx settings I used or have any other questions, let me know in the comments.

    1. 1

      Thanks for sharing. May I know where does your ssl terminate and where did you host nginx?

  14. 1

    Hey Ayyappa,

    Did you got any solid solution.
    We are also now planning to implement. By the way, allocating subdomains for users will be so complex as you need to manage SSL and cross origin login if you have organization switching feature for users.

    We had finalized 2 option
    option 1: Pathname instead of subdomain

    1. Instead of Subdomain, we will have organization pathname for each company. And data access will be done by permission management. This is the easiest way to implement.
      Only issue that we face is login and signup white label, which we may need to have a custom login and signup page for each tenant along with general login and signup page.
      Benefits is that you don't need to spend much for SSL management or a service that handle SSL.

    2. Second option: Subdomain for each tenant.
      Reverse proxy is so complex,
      Point single wildcard DNS entry to your application. And use your backend to store all the subdomains that signups and store all the valid subdomains in a datastore while user signing up. Use middleware to extract the subdomain when a request come, validate the subdomain extracted with middleware and allow access if it's validated by the datastore.
      But you need to setup subdomains in your development, testing environment as well which can be cumbersome. Managing SSL for each subdomain will be another headache or you need to automate this process also.
      Let me know your thoughts on this.

    1. 1

      We haven't implemented this yet but still looking for options. As of now approximated.app seems a close match. But will revisit and update incase if I have an update.

      1. 1

        Did you tried NGINX reverse proxy to map the clients domain and then issue ssl from your end? and ask them to point the cname. or check https://developers.cloudflare.com/ssl/ssl-for-saas#availability

      2. 1

        Thanks for that. Will post update about our implementation once its done.

  15. 1

    As others have said, Caddy is your friend here.

    I recently wrote a blog post on using Caddy to support custom domains. Hope this helps.

    CloudFlare also offers SSL for SaaS, although it requires and Enterprise plan...not cheap.

    1. 3

      Just sharing that CloudFlare has rebranded "SSL for SaaS" into "CloudFlare for SaaS" and it's now available with all plans (including their free plan).

      Price is 100 custom domains for free, then $0.10/month per additional custom domain. Definitely a no-brainer coming from such a trusted brand.

  16. 1

    setup a nginx proxy that points to your subdomain(s)

  17. 1

    Is their custom domain not pointing to you ip address? You would probably need to do the same, pass the domain info from nginx to backend server.

Trending on Indie Hackers
Meme marketing for startups 🔥 User Avatar 11 comments 40 open-source gems to replace your SaaS subscriptions 🔥 🚀 User Avatar 1 comment After 19,314 lines of code, i'm shutting down my project User Avatar 1 comment Need feedback for my product. User Avatar 1 comment We are live on Product Hunt User Avatar 1 comment Don't be a Jerk. Use this Tip Calculator. User Avatar 1 comment