Setting Up Subdomains
A subdomain is a prefix you add in front of your main domain, like api.example.com or blog.example.com. It lets you split one domain into many separate sites or services, each pointing wherever you want — a different app, a different server, or a third-party host. Subdomains are the foundation of multi-site hosting: you can run your marketing site, your API, and your blog all under one domain without buying three domains. This page shows you how to create a subdomain in DNS and how to match it in an Nginx server block so the right traffic reaches the right app.
What exactly is a subdomain
Every domain name is read right to left. In api.example.com:
comis the top-level domain (TLD) — the rightmost part.example.comis your root domain (also called the apex domain) — the name you registered.apiis the subdomain — a label you can create yourself, for free, as many as you like.
You do not register or pay for subdomains. Once you own example.com, you control everything to the left of it. You create a subdomain simply by adding a DNS record (an entry in your domain’s address book that maps a name to a destination).
The special label
wwwis itself just a subdomain.www.example.comis a subdomain ofexample.com— there is nothing magic about it.
A record vs CNAME record — which to use
To create a subdomain you add one DNS record. You have two main choices.
| Record type | Points to | When to use |
|---|---|---|
| A record | An IP address (e.g. 203.0.113.10) | The subdomain runs on a server whose IP you control. Most common for your own VPS. |
| CNAME record | Another domain name (e.g. myapp.netlify.app) | The subdomain is hosted by a third party (Netlify, Vercel, a load balancer) that gives you a hostname, not a fixed IP. |
When to use an A record: you have your own Ubuntu server with a static public IP and you are hosting the app yourself. Point the subdomain straight at the IP.
When to use a CNAME: the destination is managed by someone else and its IP can change. A CNAME follows the target name, so the provider can move their servers without breaking you. Do not put a CNAME on your root domain (example.com with no prefix) — many DNS providers forbid it because the apex must also hold other records like MX (mail). Subdomains are safe for CNAMEs.
Step 1 — create the DNS record
Log in to your DNS provider (Cloudflare, your registrar, Route 53, etc.). Add a record. The fields are the same everywhere:
For an A record pointing api at your server:
Type: A
Name: api
Value: 203.0.113.10
TTL: 3600
Name: api automatically means api.example.com — you only type the prefix, not the whole name. TTL (time to live) is how many seconds resolvers cache the answer; 3600 (one hour) is a sane default.
For a CNAME pointing blog at an external host:
Type: CNAME
Name: blog
Value: myblog.ghost.io
TTL: 3600
Save the record. DNS changes can take a few minutes to a couple of hours to spread (propagate) across the internet.
Step 2 — verify the record from your server
SSH into your Ubuntu server and confirm the subdomain resolves to the right place. Install the DNS tools if needed:
sudo apt update
sudo apt install -y dnsutils
Then query the subdomain:
dig +short api.example.com
Output:
203.0.113.10
If you get back your server’s IP, DNS is working. If you get nothing, wait a few minutes and try again — the record may still be propagating.
Step 3 — match the subdomain in Nginx
Now that api.example.com reaches your server, you need to tell Nginx which app should answer for it. Nginx (a web server and reverse proxy — a server that sits in front of your app and forwards requests to it) decides this using the server_name directive.
Create a dedicated config file for the subdomain:
sudo nano /etc/nginx/sites-available/api.example.com
Add a server block that forwards traffic to an app listening on a local port (here a Node.js API on port 3000):
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
The server_name api.example.com; line is the matching rule: when a request arrives with Host: api.example.com, this block handles it. Other subdomains fall through to their own blocks.
Enable the site by symlinking it into sites-enabled, test the config, and reload:
sudo ln -s /etc/nginx/sites-available/api.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration test is successful
Always run sudo nginx -t before reloading — it catches typos so a broken config never takes your sites down.
Multi-site hosting — many subdomains, one server
The real power is hosting several sites on one machine. Give each subdomain its own DNS record (all pointing at the same IP) and its own Nginx server block. Nginx routes by server_name:
| Subdomain | DNS record | Nginx server_name | Backend |
|---|---|---|---|
app.example.com | A → 203.0.113.10 | server_name app.example.com; | Frontend on :3001 |
api.example.com | A → 203.0.113.10 | server_name api.example.com; | API on :3000 |
blog.example.com | A → 203.0.113.10 | server_name blog.example.com; | Static files in /var/www/blog |
A blog block serving static files instead of proxying looks like this:
server {
listen 80;
server_name blog.example.com;
root /var/www/blog;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
One server, three subdomains, three completely independent sites. If your firewall is on, make sure HTTP and HTTPS are allowed:
sudo ufw allow 'Nginx Full'
After a subdomain works over plain HTTP, secure it with a free SSL certificate. Running
sudo certbot --nginx -d api.example.comadds HTTPS and edits the server block for you. Never leave an API serving real traffic on port 80 only.
Best Practices
- Use an A record for subdomains on your own server, and a CNAME for subdomains hosted by a third party — never put a CNAME on the apex domain.
- Give each subdomain its own file in
/etc/nginx/sites-availableso configs stay small and easy to manage. - Always run
sudo nginx -tbeforesudo systemctl reload nginxto catch errors before they go live. - Verify DNS with
dig +short <subdomain>before debugging Nginx — many “site down” issues are just DNS that has not propagated yet. - Keep TTL at
3600for stable records; lower it to300shortly before a planned migration so changes take effect quickly. - Add a TLS certificate to every public subdomain and redirect HTTP to HTTPS so no traffic is ever unencrypted.