Today I'll tell how do I make wildcard subdomains and how do I handle the data between multiple user account. I thought it was hard, but turns out it is not (or maybe it is easy because I doing it wrong? I don't know).
Every time I search about how to make a multi tenant website, there are 0 articles on how to build them. I always wonder how do they do their architecture etc.
The key to this type of website architecture are just wildcard subdomain and reading the host on your code. As simple as that.
The website requirement are :
- It's blog platform
- Every user will have their own subdomain (zeke.bloggy.net, eren.bloggy.net, etc)
- Every subdomain had their own data
So there are some steps.
There are some tips later on using it with cloud
Buy a Domain and VPS
First you must own a domain name and VPS (Virtual Private Server). I do think that this could be done in with serverless too, I just didn't tried that.
Point your domain to the VPS
On your domain registrar DNS Management There are 4 record that you should add. You also need your VPS IP address to point to.
| domain | record type | value | host | ttl |
|------------|-------------|------------------------------------|------------------------------------|-------|
| bloggy.com | A Record | 192.0.0.1 | bloggy.com | 14400 |
| bloggy.com | CNAME | bloggy.com | www | 14400 |
| bloggy.com | A Record | 192.0.0.1 | * | 14400 |
| bloggy.com | TXT Record | radom-value-provided-by-lestecrypt | radom-value-provided-by-lestecrypt | 14400 |
The IP address there are your VPS IP address the domain are your domain (till the end of this article we'll use bloggy.com)
The TXT Record will be given by LetsEncrypt when we need https, I'll on the next step.
Setup nginx to point to your domain
Install Nginx.
Apache, Nginx, or other web server thingy have their own syntax of configuration I used Nginx here. Use your own preferences
The configuration I use for bloggy.com
are the same as article here try to follow that article guide (I want to keep this posts as short as possible)
The difference is when I want to register the wildcard subdomain. The certbot command I used are different.
It is not :
sudo certbot --nginx -d bloggy.com -d www.bloggy.com
but this :
sudo certbot --server https://acme-v02.api.letsencrypt.org/directory -d *.example.com --manual --preferred-challenges dns-01 certonly
On this part you'll need to make the TXT Record.
I don't know why the -d *.example.com
won't work, seems like it is a domain registrar problem. I obtain this trick from this article
For now, you will have a working wildcard subdomain and domain. Whether you visit bloggy.com
, zeke.bloggy.com
, eren.bloggy.com
you will see Nginx welcome page (If you use nginx)
Handle the subdomain on your code
I use node.js (Express.js) for this, different app may have different syntax. But the rule are simple, read the hostname to get the account name and fetch the data;
The app had 2 routes home
and blog
Here the simplest code possible
const express = require('express')
const app = express()
const port = 3000
const dataBlog = [
{ account: 'zeke', blog: ['so zeke thing'] },
{ account: 'eren', blog: ['so eren thing 2'] }
]
// Home route list all blog and account
app.get('/', (req, res) => {
res.send(dataBlog)
})
// Blog route only list the blog based on host account name
// You will see that we dont use params here, we use hostname
app.get('/blog', (req, res) => {
const host = req.headers.host // or 'x-forwarded-host'
const accountName = host.toString().split('.')[0] // get the account name on domain.
if (accountName) {
const accountData = dataBlog.filter(acc => acc.account === accountName)
res.send(accountData)
} else {
res.code(404).send('Not Found')
}
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
Proxy your web app with nginx
Node.js aren't like PHP it has its own web servers, but to contact with real world it needs proxy. So we just proxy it to our nginx.
On bloggy.com
nginx configuration
# so the home route will be on bloggy.com
proxy_pass http://localhost:3000;
On the wildcard *.bloggy.com
nginx configuration
# so the blog route will point to *.bloggy.com
proxy_pass http://localhost:3000/blog;
That's it! Every time you visit bloggy.com/blog
it will respond 'not found' but if you visit zeke.bloggy.com
it will fetch the data for 'zeke'.
On Cloud
So later example are for VPS, however if you want to load balance your apps with VPS it going to be little hard (in my experience).
Better use cloud service like GKE, DO, or AWS. With K8S (Kubernetess). It is more easy. You can achieve it using ingress.
The step by step to take that are on here It is the same.
And you can just point your domain name to the ingress IP.
Here are some config
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: $BUDDY_PROJECT_NAME
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: 'letsencrypt-clusterissuer'
spec:
tls:
- hosts:
- "bloggy.com"
secretName: domain-$BUDDY_PROJECT_NAME
rules:
- host: 'bloggy.com'
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: $BUDDY_PROJECT_NAME
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rewrite-$BUDDY_PROJECT_NAME
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: 'letsencrypt-clusterissuer'
nginx.ingress.kubernetes.io/rewrite-target: /blog/$1
spec:
tls:
- hosts:
- "*.bloggy.com"
secretName: wildcard-certificate-tls
rules:
- host: '*.bloggy.com'
http:
paths:
- backend:
service:
name: $BUDDY_PROJECT_NAME
port:
number: 80
path: "/(.*)"
pathType: Prefix
---
Closing
I hope this will help you to make your multi tenant website. Have a great day. I welcome every feedback you had! :D
Top comments (1)
This is useful! Thanks very much!