Project: Multi-Domain Virtual Hosts
Introduction
One VPS often hosts multiple sites: marketing static pages on example.com and an API on api.example.com. This project configures two server blocks, separate logs, HTTPS for each, and HTTP→HTTPS redirects.
Prerequisites
- Your First Virtual Host
- HTTPS and Let's Encrypt
- DNS A records for both hostnames to your server IP
Project Layout
| Domain | Role | Backend |
|---|---|---|
example.com | Static marketing site | /var/www/marketing |
api.example.com | REST API | 127.0.0.1:8080 |
Step 1: Deploy Content
bash
sudo mkdir -p /var/www/marketing
sudo rsync -av ./marketing-site/ /var/www/marketing/
sudo chown -R www-data:www-data /var/www/marketingStart API on localhost
.Step 2: Marketing Site Config
/etc/nginx/sites-available/marketing:
nginx
Step 3: API Site Config
/etc/nginx/sites-available/api:
nginx
server {
listen 80;
server_name api.example.com;
access_log /var/log/nginx/api.access.log;
error_log /var/log/nginx/api.error.log;
location / {
proxy_pass http://127.0.0.1:8080;
include /etc/nginx/proxy_params;
}
}Enable both:
bash
sudo ln -sf /etc/nginx/sites-available/marketing /etc/nginx/sites-enabled/
sudo ln -sf /etc/nginx/sites-available/api /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginxTest with Host headers:
bash
curl -I -H "Host: example.com" http://127.0.0.1/
curl -I -H "Host: api.example.com" http://127.0.0.1/healthStep 4: TLS for Both
bash
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot --nginx -d api.example.comOr one cert with all names if preferred:
bash
sudo certbot --nginx -d example.com -d www.example.com -d api.example.comVerify redirects:
bash
curl -I http://example.com/
curl -I https://api.example.com/Step 5: Canonical www Redirect (Optional)
After TLS, redirect www to apex:
nginx
server {
listen 443 ssl http2;
server_name www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
return 301 https://example.com$request_uri;
}Log Separation Benefits
Tail one site without noise:
bash
sudo tail -f /var/log/nginx/api.access.log
sudo tail -f /var/log/nginx/marketing.access.logAcceptance Checklist
-
https://example.comserves static site -
https://api.example.comproxies to JVM/Node - Separate access logs receive entries
- HTTP redirects to HTTPS on both
- Wrong
Hoston IP does not leak wrong site (optional default_server hardening)
FAQ
One cert or two?
One SAN cert is simpler to renew; separate certs isolate expiry mistakes.
Third subdomain?
Add third file in sites-available + Certbot -d.
Same API from SPA domain?
Use project 25 single-domain pattern instead—or CORS across subdomains.
IPv6?
Add listen [::]:443 ssl http2; in each SSL server block.