One VPS with one IP address can host many websites, each with its own domain and its own free HTTPS certificate. Modern browsers send the requested hostname during the TLS handshake (SNI, Server Name Indication), so Nginx can pick the right certificate for each domain. Here’s a clean setup.
How it works
- DNS for each domain points to the same server IP.
- Nginx has one server block per domain, each with its own
server_name, web root (or app), and certificate. - During the TLS handshake, the client says which hostname it wants (SNI), and Nginx presents the matching certificate.
1. DNS
For each domain, add A records (example.com and www.example.com, othersite.org, …) pointing to your server. See point a domain to a VPS.
2. One server block per site
# /etc/nginx/sites-available/example.com
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com/public;
index index.html index.php;
location / { try_files $uri $uri/ =404; }
}
# /etc/nginx/sites-available/othersite.org
server {
listen 80;
server_name othersite.org www.othersite.org;
location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; }
}
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/othersite.org /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Use separate Linux users and PHP-FPM pools per site, if the sites belong to different owners. See Nginx server blocks and PHP-FPM tuning.
3. Certificates
One certificate per site (simplest to manage):
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot --nginx -d othersite.org -d www.othersite.org
Certbot adds listen 443 ssl, the certificate paths, and an HTTP→HTTPS redirect to each server block.
One certificate covering several domains (SAN certificate):
sudo certbot --nginx -d example.com -d www.example.com -d othersite.org -d www.othersite.org
Fewer certificates, but all names appear in one certificate, and a validation failure for one domain blocks renewal for all of them. Usually, one per site is better.
See Let’s Encrypt with Certbot.
4. Check renewals
sudo certbot certificates # list certificates, names, and expiry dates
sudo certbot renew --dry-run
systemctl list-timers | grep certbot
Add expiry monitoring for every domain (Uptime Kuma can check certificates). See Uptime Kuma.
5. Redirects and canonical host
Pick one canonical host per site (with or without www), and redirect the other. See redirect www to non-www and Nginx HTTP to HTTPS redirect.
6. Default server: avoid serving the wrong site
If a request arrives for a hostname with no matching server block (or for the raw IP), Nginx uses the default server for that port: by default the first one loaded. Users might see another customer’s site and certificate. Define an explicit default:
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _;
ssl_reject_handshake on;
return 444;
}
Troubleshooting
| Symptom | Cause |
|---|---|
| “Certificate is for another domain” | Missing server block or certificate for that name; request hit the default server |
| Certbot “unauthorized” | DNS doesn’t point here yet, or port 80 is blocked |
| One site works, the other redirects to it | Duplicate server_name, or a redirect rule matching too broadly |
| Mixed content | The app generates http:// URLs; set X-Forwarded-Proto, fix base URLs |
Test what a server presents for each name:
openssl s_client -connect your-server-ip:443 -servername othersite.org </dev/null 2>/dev/null | openssl x509 -noout -subject -dates
See SSL certificate chain errors.
Quick reference
| Task | How |
|---|---|
| Per-site config | One server block per domain |
| Certificates | certbot --nginx -d domain -d www.domain |
| List certs | certbot certificates |
| Test renewal | certbot renew --dry-run |
| Safety | Explicit default_server |
Related: Nginx subdomains and VPS for agencies.