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

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:

```java
businessname.yourapp.com
```

Example for a salon SaaS:

```java
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:

```java
saloonking.yourapp.com
```

The browser sends a request where:

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

Your Express server checks the database:

```java
subdomain "saloonking" → matches Tenant A
```

Then React loads that tenant’s website data.

---

# PROJECT STRUCTURE (MERN)

```java
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`

```java
const mongoose = require("mongoose");

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

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

Example record:

```java
{
  "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`

```java
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"));
```

---

3. # **React Frontend Logic (Auto-Detect Subdomain)**
    

`frontend/src/App.js`

```java
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`

```java
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`

```java
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`

```java
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:

```java
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:

```java
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:

```java
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:

| Type | Name | Value |
| --- | --- | --- |
| **A** | `*.`[`yourapp.com`](http://yourapp.com) | Your server IP |
| **A** | [`yourapp.com`](http://yourapp.com) | Your server IP |

Example:

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

This means:

> ANY subdomain → points to your backend server.

So:

* [`saloon1.yourapp.com`](http://saloon1.yourapp.com) → goes to your Express app
    
* [`saloon2.yourapp.com`](http://saloon2.yourapp.com) → goes to your Express app
    
* [`xyz.yourapp.com`](http://xyz.yourapp.com) → also goes to your Express app
    

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:

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

await newTenant.save();
```

Now [`saloonName.yourapp.com`](http://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.
