Skip to main content

Command Palette

Search for a command to run...

Building a Multi-Tenant SaaS With Automatic Subdomains Using MERN Stack (2025 Guide)

Updated
4 min read
Building a Multi-Tenant SaaS With Automatic Subdomains Using MERN Stack (2025 Guide)
A

Hi there! I'm Aditya, a passionate Full-Stack Developer driven by a love for turning concepts into captivating digital experiences. With a blend of creativity and technical expertise, I specialize in crafting user-friendly websites and applications that leave a lasting impression. Let's connect and bring your digital vision to life!

Modern SaaS applications—LMS platforms, CRM tools, appointment management systems—often need multi-tenant architecture. A powerful (and popular) approach is subdomain-based multi-tenancy, where each business gets:

businessname.yourapp.com

Example for a salon SaaS:

saloonking.glowSaaS.com
prettylooks.glowSaaS.com
divasalon.glowSaaS.com

In this guide, we will build a working subdomain multi-tenant system using:

  • MongoDB

  • Express.js

  • React

  • CORS

  • Local host mapping

  • DNS configuration for production

Everything is simplified so you can extend later.


How Subdomain Multi-Tenancy Works

When someone enters:

saloonking.yourapp.com

The browser sends a request where:

req.hostname = saloonking.yourapp.com
subdomain = saloonking

Your Express server checks the database:

subdomain "saloonking" → matches Tenant A

Then React loads that tenant’s website data.


PROJECT STRUCTURE (MERN)

root/
  backend/
    server.js
    models/Tenant.js
  frontend/
    src/
      pages/
        TenantSite.jsx
        MainSite.jsx
        Dashboard.jsx
    App.js

Backend port: 5000
Frontend port: 3000


1. MongoDB Tenant Model

backend/models/Tenant.js

const mongoose = require("mongoose");

const tenantSchema = new mongoose.Schema({
  name: String,
  subdomain: String,
  siteData: Object
});

module.exports = mongoose.model("Tenant", tenantSchema);

Example record:

{
  "name": "Saloon King",
  "subdomain": "saloonking",
  "siteData": {
    "title": "Welcome to Saloon King",
    "description": "Best salon in town"
  }
}

2. Express Backend With Subdomain Detection

backend/server.js

const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const Tenant = require("./models/Tenant");

const app = express();
app.use(cors());
app.use(express.json());

mongoose.connect("mongodb://127.0.0.1:27017/multitenant");

// Middleware: detect subdomain
app.use(async (req, res, next) => {
  const host = req.hostname; // e.g., saloonking.yourapp.com
  const parts = host.split(".");
  const sub = parts[0];

  if (sub !== "localhost" && sub !== "yourapp" && sub !== "app") {
    const tenant = await Tenant.findOne({ subdomain: sub });
    if (tenant) req.tenant = tenant;
  }

  next();
});

// API to return tenant data
app.get("/tenant", (req, res) => {
  if (!req.tenant) return res.json({ tenant: null });
  res.json(req.tenant);
});

app.listen(5000, () => console.log("Backend running on port 5000"));

  1. React Frontend Logic (Auto-Detect Subdomain)

frontend/src/App.js

import TenantSite from "./pages/TenantSite";
import MainSite from "./pages/MainSite";
import Dashboard from "./pages/Dashboard";

function App() {
  const hostname = window.location.hostname.split(".")[0];

  if (hostname === "app") return <Dashboard />;
  if (hostname === "yourapp" || hostname === "localhost")
    return <MainSite />;

  return <TenantSite />;
}

export default App;

📄 Tenant Website Page

frontend/src/pages/TenantSite.jsx

import { useEffect, useState } from "react";

export default function TenantSite() {
  const [tenant, setTenant] = useState(null);

  useEffect(() => {
    fetch("http://localhost:5000/tenant")
      .then(res => res.json())
      .then(data => setTenant(data));
  }, []);

  if (!tenant) return <h2>Loading...</h2>;

  return (
    <div style={{ padding: 20 }}>
      <h1>{tenant.siteData.title}</h1>
      <p>{tenant.siteData.description}</p>
    </div>
  );
}

🏠 Main SaaS Homepage

frontend/src/pages/MainSite.jsx

export default function MainSite() {
  return (
    <div style={{ padding: 20 }}>
      <h1>Welcome to My SaaS</h1>
      <p>Create your own salon website in minutes.</p>
    </div>
  );
}

🧑‍💼 Tenant Dashboard

frontend/src/pages/Dashboard.jsx

export default function Dashboard() {
  return (
    <div style={{ padding: 20 }}>
      <h1>Tenant Dashboard</h1>
      <p>Edit your website here</p>
    </div>
  );
}

🧪 4. Local Testing: Configure /etc/hosts

Add:

127.0.0.1 saloonking.localhost
127.0.0.1 prettylooks.localhost
127.0.0.1 yourapp.localhost
127.0.0.1 app.localhost

Now test:

http://saloonking.localhost:3000

You will see that salon’s website.


🌐 5. DNS + Production Setup: Allow Users to Create Subdomains Automatically

This is the MOST IMPORTANT part you requested.
This lets your SaaS automatically create:

saloonname.yourapp.com

✔ Only ONE DNS record is required:

Step 1: Add a Wildcard DNS Record

Go to your domain provider (GoDaddy / Hostinger / Cloudflare).

Add:

TypeNameValue
A*.yourapp.comYour server IP
Ayourapp.comYour server IP

Example:

Type: A
Host: *
Value: 34.101.23.88 (your VPS IP)
TTL: Auto

This means:

ANY subdomain → points to your backend server.

So:

You do NOT need to create each subdomain manually.

🎉 This is how SaaS platforms like Shopify, Wix, and Notion do it.


🏗 Step 2: When a salon signs up, generate a subdomain

In your signup logic:

const newTenant = new Tenant({
  name: req.body.name,
  subdomain: req.body.subdomain,
  siteData: { title: "", description: "" }
});

await newTenant.save();

Now saloonName.yourapp.com will start working immediately because of the wildcard DNS.


🔒 Step 3: SSL (HTTPS)

For HTTPS, use:

Caddy (best and automatic)

or

✔ Certbot + NGINX

or

✔ Cloudflare proxy

Caddy auto-generates SSL for ALL wildcard subdomains.

More from this blog

A

Aditya Kumar Gupta

23 posts

Hi there! I'm Aditya, a passionate Full-Stack Developer driven by a love for turning concepts into captivating digital experiences. With a blend of creativity and technical expertise