Nginx is the most widely used web server on VPS: lightweight, fast, and able to serve a static site as well as a PHP, Node.js or Python application. This guide installs it, creates a first site and switches it to HTTPS with a free Let's Encrypt certificate renewed automatically. Allow ten minutes on Ubuntu 24.04 or Debian 12.
1. Prerequisites
- A VPS with root access over SSH. See first steps on your VPS.
- A domain whose
Arecords (andAAAAif you have IPv6) point to the VPS. Check before you start:
dig +short mysite.com
dig +short www.mysite.com
Both commands must return your VPS IP address. If not, wait for propagation or fix your DNS: see configuring DNS. Let's Encrypt will refuse the certificate as long as the domain does not point to the server.
2. Install Nginx
apt update
apt install -y nginx
systemctl enable --now nginx
If the UFW firewall is active, open ports 80 and 443. Port 80 must stay open even after switching to HTTPS: Let's Encrypt uses it to verify the domain at every renewal.
ufw allow 'Nginx Full'
Opening http://IP_ADDRESS in a browser should show the "Welcome to nginx" page. For the firewall configuration, see configuring UFW.
3. Create the site
Nginx reads one configuration file per site in /etc/nginx/sites-available/, enabled by a link in sites-enabled/. Create the site folder and a test page:
mkdir -p /var/www/mysite.com
echo '<h1>It works</h1>' > /var/www/mysite.com/index.html
chown -R www-data:www-data /var/www/mysite.com
Then the file /etc/nginx/sites-available/mysite.com:
server {
listen 80;
listen [::]:80;
server_name mysite.com www.mysite.com;
root /var/www/mysite.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
ln -s /etc/nginx/sites-available/mysite.com /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx
nginx -t before reload. If there is a syntax error, Nginx tells you the file and line, and the live site is not interrupted.For a PHP application, see installing WordPress; for Node.js, Node.js with PM2 and Nginx.
4. Get the certificate
apt install -y certbot python3-certbot-nginx
certbot --nginx -d mysite.com -d www.mysite.com --redirect -m [email protected] --agree-tos
Certbot contacts Let's Encrypt, which checks that you control the domain by requesting a temporary file on port 80. Once the certificate is issued, Certbot edits your Nginx file: it adds listening on port 443, the certificate paths, and a permanent redirect from HTTP to HTTPS thanks to --redirect.
The email address is only used for expiry warnings, should automatic renewal ever fail.
5. Check automatic renewal
Let's Encrypt certificates are valid for 90 days. The Certbot package installs a scheduled task that renews them automatically when less than 30 days remain. Check that it is active, then simulate a renewal:
systemctl list-timers | grep certbot
certbot renew --dry-run
If the simulation succeeds, there is nothing more to do. Let's Encrypt has announced shorter validity periods in the coming years, which makes this automation essential.
Certbot already reloads Nginx after each renewal with the Nginx plugin. If you use the certificate elsewhere, in a mail server for example, add a script to /etc/letsencrypt/renewal-hooks/deploy/.
6. HTTP/2 and security headers
HTTP/2 loads several files in parallel over a single connection. The syntax depends on your Nginx version, shown by nginx -v:
# Nginx 1.25.1 and newer
listen 443 ssl;
http2 on;
# Nginx 1.24 (Ubuntu 24.04) and older
listen 443 ssl http2;
Then add these headers to the HTTPS server block:
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000" always;
Strict-Transport-Security (HSTS) header forces browsers to use HTTPS only for the given duration, here one year. Only enable it once HTTPS is stable on all the subdomains concerned: if you roll back, visitors would no longer be able to reach the site.Test your configuration with the free SSL Labs service: a site configured this way gets an A rating.
7. Behind Cloudflare
If your domain goes through the Cloudflare proxy (orange cloud), verification on port 80 usually works, but two settings are worth knowing:
- In Cloudflare, choose the Full (strict) SSL mode. The "Flexible" mode leaves traffic unencrypted between Cloudflare and your server, and often causes redirect loops.
- If you enable "Always Use HTTPS" or redirect rules at Cloudflare, let the
/.well-known/acme-challenge/path through.
Details in Cloudflare: DNS and proxy.
8. Troubleshooting
| Message | Likely cause | Solution |
|---|---|---|
Timeout during connect | Port 80 closed | Open it in UFW and check that no other firewall blocks it. |
NXDOMAIN or DNS problem | The domain does not point to the VPS yet | Check with dig and wait for propagation. |
Invalid response ... 404 | Another server block answers instead of yours | Check server_name and remove the default site. |
too many certificates already issued | Let's Encrypt limit reached after too many attempts | Wait, and test with --dry-run before trying again. |
Certbot's detailed logs are in /var/log/letsencrypt/letsencrypt.log.
Frequently asked questions
Is the Let's Encrypt certificate really free?
Yes. Let's Encrypt is a non-profit certificate authority. Its certificates are trusted by every browser and provide the same encryption as a paid certificate. Paid certificates mainly differ by organisation validation and financial warranty, rarely useful for an ordinary website.
Can I get a certificate for every subdomain?
Yes, with a wildcard certificate for *.mysite.com. It requires validation through a DNS record rather than port 80, and therefore a Certbot plugin compatible with your DNS provider, for example Cloudflare. For a few subdomains, it is simpler to list them with several -d options.
What happens if renewal fails?
Nothing immediate: renewal starts 30 days before expiry and is retried automatically. If the failure persists, you receive a warning email at the address you gave. The usual causes are a closed port 80 or a domain that no longer points to the server.
Should port 80 stay open after switching to HTTPS?
Yes. It redirects visitors who type the address without https, and Let's Encrypt needs it to validate every renewal. It poses no risk as long as it only redirects to HTTPS.



