Your Platform With Custom Domains
A brief guide on supporting custom domains within your web application
Back in 2012, I had an app idea. I wanted to build the next social network. I was barely 17 years old, and I was beginning my coding career.
I ran into a problem… how do I let people point their domain names to my service, and use it for their profile pages?
At the time I barely understood how DNS works. This article won’t get into that too much, but I will give you a couple resources if you’re interested in diving deep to understand DNS at a low level.
I highly recommend this guide on building your own DNS server in Rust. The author does an amazing job at explaining how the protocol works, how to parse DNS packets, and how it all works. There’s many other high level guides on the subject, but I find seeing some of the code can help you get a deeper understanding.
With that out of the way, let’s get started!
TypeScript + Node
For this very simple example, we are going to make a new TS project. I like to use yarn, and I tried to keep this incredibly simple. So here’s what I did:
yarn init # hit enter until it completes
yarn add ts-node typescript express @types/express
People like to bash the amount of dependencies TS apps have, well, this is all we need today, four of them.
Next, we will create a server.ts file:
import express from "express";
const app = express();
app.get("/", (req, res) => {
res.send("Hello world!");
});
const port = 3000;
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
If it’s your first time seeing this code, it’s not too hard to understand. We create an app using express. Whenever an http request is made to the root of our server “/”, we respond with “Hello World.”
At the end of the file, we start listening for connections on port 3000.
You can now run
yarn ts-node server.ts
Open your browser to localhost:3000, and you will see “Hello World” printed to the screen!
Supporting multiple domains
As I said before, I’m going to keep this very simple, so let’s add one more method to the file, and handle returning different content depending on the domain name that is being used!
const users = [
{
name: "Zach",
domain: "zach.com:3000",
about: "Zach is the best tutorial creator ever",
},
{
name: "Mike",
domain: "mike.com:3000",
about: "Mike is the best new developer ever",
},
];
By adding this to the file, we are mimicking what might be stored in a database. In a real application you will need to let a user login to their account, and add their custom domain to your application and save it somewhere.
For now, we are hard coding this.
There’s only two more steps until we can see the magic happen! Let’s add a new route for /about.
app.get("/about", (req, res) => {
let host = req.get("host");
let user = users.find((user) => user.domain === host);
if (user) {
return res.send(user.about);
}
res.send(
"Oh no, we don't have an about page for this user."
);
});
Here’s all the magic. When the browser makes a request to a server, it will pass along a host header. So if you type in `mike.com:3000` it’s going to set that in the host header, and make a request to the server.
We attempt to find a user in our list with a matching host. If it’s found, we return the about text from the user object. Otherwise, we return an error notice.
Setting the DNS rules
At this stage, we just need to point `zach.com` and `mike.com` to our local computer. On Linux / Mac based machines, you need to edit this file:
sudo vi /etc/hosts
We need to add two new entries to the file:
127.0.0.1 zach.com
127.0.0.1 mike.com
Testing it out
With this in place, we need to restart the server. Close the existing instance and run:
yarn ts-node server.ts
Here’s what you should see if you open it up to zach.com/about:
mike.com:
And a normal request to localhost:
You can find the complete code on GitHub if you got lost.
We are able to have multiple sites, pointing to the same server, and behind the scenes we can return completely different content.
Next Steps
You might be wondering “why did we put :3000 on the end.” This is only for testing purposes. A production website is exposed on :80 and :443, which will work without specifying a port number on the end.
Another thing to note, is how /etc/hosts works. It’s a place for local host rules that only work on your computer. If you want to use this same approach on a production site, you will need to have your server hosted somewhere with a dedicated IP address. This will let users add an A, or address record pointing their domain to your server’s IP.
Once that is done, every piece of the code we wrote today would work the exact same. You can remove the :3000 off the end of course.
I hope you found this helpful! I wish I had found a post like this back in 2012. If you have further questions on this topic, feel free to leave a reply.
Have fun adding custom domains to your applications!