How to generate a free SSL certificate with Let's Encrypt (step-by-step)

Redes & Segurança · By · Published on · Updated on · 7 min read

Close-up of a screen showing colorized code in an editor, with selective focus and a blurred background
Good for setup tutorials, servers, and SSL on Linux. Photo: Godfrey Atima / Pexels

This tutorial shows how to generate a free SSL certificate with Let's Encrypt using Certbot. We cover installation, issuance with HTTP-01 or DNS-01, Nginx/Apache configuration, renewal and verification. Requires root access and care to avoid interrupting services. The walkthrough is practical, concise, and works for most Linux servers.

Prerequisites and when to use Let's Encrypt

Before you start, make sure your domain points to the server and that you have administrative permissions. For public sites, blogs, APIs and stores, Let’s Encrypt provides Domain Validation (DV) certificates that are suitable in most cases. For internal environments without public access, prefer DNS-01.

Essential prerequisites:

  • Active domain (e.g., example.com) pointing to the server.
  • Root or sudo access to the Linux server.
  • Ports 80 (HTTP) and 443 (HTTPS) open in firewall and cloud settings.
  • Web server (Nginx or Apache) installed, or the ability to stop it for standalone mode.
  • DNS propagation time (wait minutes to hours after changing A/AAAA/CNAME).

Choosing the challenge: HTTP-01 or DNS-01

Let’s Encrypt validates domain control in two main ways. Choose based on your scenario. Wildcards (*.example.com) require DNS-01.

If you also need to protect APIs with best practices like authentication and rate limiting, dig deeper into protecting APIs.

Method When to use Requirements Notes
HTTP-01 Public site responding on port 80 Port 80 open; content accessible Simpler; works with webroot, nginx or apache plugins
DNS-01 Wildcard, services behind a proxy/CDN, or no port 80 Access to DNS to create TXT records Automate via your DNS provider API when possible

Install Certbot on the server

Certbot is the recommended client to obtain and renew certificates. Below are options for Ubuntu/Debian (snap) and RHEL/CentOS (dnf). Run as root or prefix commands with sudo.

# Ubuntu/Debian via snap (recomendado)
sudo snap install core && sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
certbot --version

# Alternativa Ubuntu/Debian via APT (se não usar snap)
sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx python3-certbot-apache

# RHEL/CentOS 8/9
sudo dnf install -y epel-release
sudo dnf install -y certbot python3-certbot-nginx python3-certbot-apache
certbot --version

Issue the certificate with Certbot

Choose the option that best fits your case. For HTTP-01 you can use the Nginx/Apache plugin or webroot mode. Standalone mode starts a temporary server on port 80. Stopping the web server will cause temporary downtime.

# Substitua "exemplo.com" e adicione -d para cada domínio/subdomínio

# 1) Plugin Nginx (detecta e configura automaticamente)
sudo certbot --nginx -d exemplo.com -d www.exemplo.com

# 2) Plugin Apache
sudo certbot --apache -d exemplo.com -d www.exemplo.com

# 3) Somente certificado (webroot): informe a raiz pública do site
sudo certbot certonly --webroot -w /var/www/html \
  -d exemplo.com -d www.exemplo.com

# 4) Standalone (para quando não há servidor web ativo)
# ATENÇÃO: isso usa a porta 80 temporariamente
sudo systemctl stop nginx || true
sudo systemctl stop apache2 httpd || true
sudo certbot certonly --standalone -d exemplo.com
sudo systemctl start nginx || true
sudo systemctl start apache2 httpd || true

Configure HTTPS in Nginx or Apache

After issuance, files are located in /etc/letsencrypt/live/YOUR_DOMAIN/. Below are example configurations. Adjust paths and upstreams to your environment.

You can also automate reloads after deploy in your pipeline. See ideas in CI/CD with GitHub Actions.

# Nginx: redirecionar HTTP -> HTTPS
server {
  listen 80;
  server_name exemplo.com www.exemplo.com;
  return 301 https://$host$request_uri;
}

# Nginx: servidor HTTPS
server {
  listen 443 ssl http2;
  server_name exemplo.com www.exemplo.com;

  ssl_certificate     /etc/letsencrypt/live/exemplo.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/exemplo.com/privkey.pem;
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers on;

  root /var/www/html;
  index index.html index.php;
  location / {
    try_files $uri $uri/ =404;
  }
}

# Apache: habilitar sites com SSL (exemplo mínimo)
# Arquivo: /etc/apache2/sites-available/exemplo-ssl.conf
# Habilite módulos: a2enmod ssl rewrite && a2ensite exemplo-ssl && systemctl reload apache2
<VirtualHost *:443>
  ServerName exemplo.com
  ServerAlias www.exemplo.com
  DocumentRoot /var/www/html

  SSLEngine on
  SSLCertificateFile /etc/letsencrypt/live/exemplo.com/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/exemplo.com/privkey.pem
</VirtualHost>

# Redireciono HTTP para HTTPS (Apache):
<VirtualHost *:80>
  ServerName exemplo.com
  ServerAlias www.exemplo.com
  Redirect "/" "https://exemplo.com/"
</VirtualHost>

Automatic renewal and testing the process

Let's Encrypt certificates expire in about 90 days. Certbot installs a systemd timer or you can schedule renewal via cron. Test renewal safely with --dry-run.

After renewal, reload the web server to apply the new certificate.

# Testar renovação (sem emitir de fato)
sudo certbot renew --dry-run

# Ver timers do systemd relacionados ao certbot
systemctl list-timers | grep -i certbot || true

# Cron alternativo (executa 2x ao dia)
# /etc/crontab
# 0 3,15 * * * root certbot renew --quiet --deploy-hook "systemctl reload nginx || systemctl reload apache2 || systemctl reload httpd"

How to confirm it worked

Validate HTTPS in a browser and from the command line. Check headers, issuer, dates, and the certificate chain. Confirm that HTTP-to-HTTPS 301 redirects are active.

For continuous reliability, consider 24/7 Linux server monitoring.

# Verificar resposta e cabeçalhos
curl -I https://exemplo.com

# Conferir certificado apresentado (emissor, sujeito e validade)
openssl s_client -connect exemplo.com:443 -servername exemplo.com </dev/null \
  | openssl x509 -noout -issuer -subject -dates

Common troubleshooting

See frequent issues and checks. Adjust according to your distro and network environment.

- HTTP-01 challenge fails: port 80 blocked, wrong webroot, proxy blocking access. - DNS hasn't propagated: wait for TTL; verify A/AAAA/CNAME records. - Wildcard: use DNS-01. - Reverse proxy/load balancer: run Certbot at the edge or use DNS-01. - Permissions: certificate paths must be readable by the web service.

  • Open ports 80/443 in firewall and cloud settings.
  • Confirm the webroot path; test a file under .well-known/acme-challenge/.
  • Check Certbot logs for detailed messages.
  • Verify DNS with dig/nslookup.
  • If needed, switch to DNS-01 and automate via your DNS provider API.
# Portas abertas localmente?
sudo ss -tulpen | egrep ':80|:443' || true

# UFW (Ubuntu):
sudo ufw allow 'Nginx Full' || sudo ufw allow 80,443/tcp

# firewalld (RHEL/CentOS):
sudo firewall-cmd --add-service=http --add-service=https --permanent
sudo firewall-cmd --reload

# Logs do Certbot
tail -n 100 -f /var/log/letsencrypt/letsencrypt.log

# Verificar DNS (substitua pelo seu domínio)
dig +short A exemplo.com || nslookup exemplo.com

# Testar acesso ao desafio HTTP-01 (crie um arquivo e acesse via navegador)
mkdir -p /var/www/html/.well-known/acme-challenge/
echo ok > /var/www/html/.well-known/acme-challenge/teste
curl http://exemplo.com/.well-known/acme-challenge/teste

Next steps and ongoing security

Keep the server updated, disable insecure protocols, and review security headers (HSTS, X-Frame-Options, etc.). If you find app weaknesses, prioritize fixes.

For help with risk diagnostics, see resources about fixing web app vulnerabilities.

Frequently asked questions

Is Let's Encrypt reliable for production?

Yes. It's a widely recognized Certificate Authority issuing DV certificates. For most public sites and APIs, DV is sufficient. If you need organizational/extended validation, seek CAs that offer OV/EV.

Can I issue a wildcard certificate (*.domain.com)?

Yes, with the DNS-01 challenge. You must create (or automate) TXT records in the domain's DNS. Many DNS providers offer APIs to automate this with Certbot.

Do I need to open port 80 for validation?

For HTTP-01, yes. The validation fetches a token via http://your-domain/.well-known/acme-challenge/. Alternatives are DNS-01 or standalone mode, provided port 80 is temporarily free for Certbot.

How do I renew without downtime?

Use Certbot with webroot or the nginx/apache plugins and configure a deploy-hook to reload the service (reload instead of restart). Reload applies the new certificate without dropping active connections on most servers.

Can I include multiple domains in the same certificate?

Yes. Add multiple -d options to the Certbot command (e.g., -d example.com -d www.example.com -d app.example.com). Only include domains that point to the same server or use DNS-01.

Can I use a staging environment to avoid rate limits?

Yes. Use --staging in Certbot commands to get test certificates, useful during deployment and automation. Remove --staging in production to get valid certificates.

Need help with a more complex setup?

Environments with reverse proxies, load balancers, containers or multiple domains may require DNS-01 and automation. If you want ad-hoc support for configuration, let's talk.

Discuss your setup

See also