Set up a web server with HTTPS
nginx with Let's Encrypt or Caddy with automatic HTTPS, plus domain and firewall.
6 min read
On this page
This guide puts a website with a valid certificate on your VPS. Pick one of two web servers:
- nginx with certbot for the Let's Encrypt certificate. Widely used, lots of guides online.
- Caddy. Fetches and renews the certificate on its own, with a shorter configuration.
Covers Debian 12 and 13, Ubuntu 22.04 and 24.04, AlmaLinux 9 and Rocky Linux 9. All commands run as root. How to log in is explained in Connect via SSH. We use the domain example.com as an example; replace it with your own everywhere.
Point the domain at your VPS
Let's Encrypt checks that the domain really points to your server, so this has to be right first.
- In the dashboard, look up your VPS's IPv4 and IPv6 address under Network.
- Create an A record pointing to the IPv4 and an AAAA record pointing to the IPv6 for
example.comand, if you need it,www.example.com. For a CitexNode domain, see Managing DNS records. - Check on the server whether the records are visible yet:
getent ahosts example.comOnce your addresses show up, continue. New records sometimes take a few minutes.
Firewall: open ports 80 and 443
Let's Encrypt needs port 80 for the check, port 443 is HTTPS. If your system runs a firewall, open both. How to set one up is covered in Set up a firewall.
Debian and Ubuntu with ufw:
ufw allow 80/tcp
ufw allow 443/tcp
ufw statusAlmaLinux and Rocky Linux with firewalld:
firewall-cmd --permanent --add-service=http --add-service=https
firewall-cmd --reloadOption A: nginx with certbot
Install nginx
Debian and Ubuntu:
apt update
apt install -y nginx certbot python3-certbot-nginxAlmaLinux and Rocky Linux (certbot comes from EPEL):
dnf install -y epel-release
dnf install -y nginx certbot python3-certbot-nginx
systemctl enable --now nginxCreate the site
Create a folder for the files and a test page:
mkdir -p /var/www/example.com
echo '<h1>It works</h1>' > /var/www/example.com/index.htmlThen the configuration /etc/nginx/conf.d/example.com.conf. All the systems above read the conf.d folder:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}Test the configuration and reload:
nginx -t
systemctl reload nginxOn AlmaLinux and Rocky Linux with SELinux, give the new folder the right context:
restorecon -Rv /var/www/example.comGet the certificate
certbot --nginx -d example.com -d www.example.comcertbot asks for an email address for expiry warnings and for the Let's Encrypt terms. It then adds the certificate to your nginx configuration and redirects HTTP to HTTPS.
The certificate is valid for 90 days. certbot renews it itself via a systemd timer. To check that renewal would work:
certbot renew --dry-runOption B: Caddy with automatic HTTPS
Install Caddy
Debian and Ubuntu, from the official Caddy repository:
apt install -y debian-keyring debian-archive-keyring apt-transport-https curl gnupg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' -o /etc/apt/sources.list.d/caddy-stable.list
chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg /etc/apt/sources.list.d/caddy-stable.list
apt update
apt install -y caddyAlmaLinux and Rocky Linux, via the official COPR repository:
dnf install -y 'dnf-command(copr)'
dnf copr enable -y @caddy/caddy
dnf install -y caddy
systemctl enable --now caddyCreate the site
mkdir -p /var/www/example.com
echo '<h1>It works</h1>' > /var/www/example.com/index.htmlReplace the contents of /etc/caddy/Caddyfile with:
example.com, www.example.com {
root * /var/www/example.com
file_server
}Validate and reload:
caddy validate --config /etc/caddy/Caddyfile
systemctl reload caddyCaddy fetches the certificate on first load, redirects HTTP to HTTPS and renews it in time. Nothing else to do.
On AlmaLinux and Rocky Linux with SELinux: restorecon -Rv /var/www/example.com.
Check that it worked
From your PC or the server:
curl -I https://example.comThe first line should be HTTP/2 200 or HTTP/1.1 200 OK. curl -I http://example.com should redirect to https:// with 301 or 308. In the browser you see the padlock next to the address.
Common errors
- certbot or Caddy reports "Timeout during connect" or "unauthorized". Let's Encrypt cannot reach your server on port 80. Check the A and AAAA records and the firewall. If the AAAA record is wrong, delete it or set up IPv6.
- "too many certificates already issued". Let's Encrypt limits how often you can get a certificate for the same domain. Test with
certbot --dry-runor wait until the limit resets. nginx -treports "conflicting server name". The name appears in two configurations. Remove the duplicate.bind() to 0.0.0.0:80 failed (Address already in use). Another service, often the second web server, holds the port.ss -tlnp | grep ':80'shows which one.- 403 Forbidden on AlmaLinux or Rocky. Usually SELinux. Run
restorecon -Rv /var/www/example.com.
What next
- Add a database: Set up MariaDB.
- WordPress on this web server: PHP and WordPress.
- Back up the server regularly: Set up backups.
If you would rather not run the server yourself, there is CitexNode Deploy; web hosting is coming soon. If you get stuck, open a ticket.