HTTPS Setup with Let's Encrypt and Certbot

Khimananda Oli 7 min read Security
HTTPS Setup with Let's Encrypt and Certbot

By Khimananda Oli | Last reviewed: August 2026

Securing web traffic is no longer optional; browsers flag unencrypted sites as unsafe, and search engines penalize them in rankings. A proper HTTPS setup with Let's Encrypt and Certbot provides free, automated TLS certificates that satisfy modern security standards without manual renewal overhead. This guide walks you through the complete implementation on Ubuntu and Nginx, from initial installation to hardened production configuration.

How does HTTPS setup with Let's Encrypt and Certbot work?

Understanding the ACME (Automated Certificate Management Environment) protocol prevents misconfiguration later. When you request a certificate, Certbot must prove domain ownership to Let's Encrypt before issuance occurs. The most common validation method for web servers is HTTP-01, where Certbot places a temporary challenge file at /.well-known/acme-challenge/ that Let's Encrypt retrieves via HTTP to verify control.

Certbot ClientUbuntu ServerLet's Encrypt CAACME ServerPublic InternetValidation Request1. CSR + Challenge2. HTTP GET Token3. Token Verified4. Signed CertificateResult: Valid TLS cert installed at /etc/letsencrypt/live/domain/fullchain.pem
ACME HTTP-01 challenge flow during HTTPS setup with Let's Encrypt and Certbot

This validation requires port 80 to be accessible from the internet. If your firewall blocks inbound HTTP or your DNS doesn't resolve correctly to this server, validation fails immediately. Before starting any free SSL setup, confirm your domain resolves to the correct IP and that UFW allows ports 80 and 443. For deeper network preparation, review the Ubuntu network troubleshooting guide if connectivity issues arise.

How do you install and configure Certbot on Ubuntu?

The official Certbot PPA provides the latest stable release, which matters because Let's Encrypt periodically deprecates older ACME protocol versions. On Ubuntu 22.04 or 24.04, use the following sequence:

sudo apt update
sudo apt install -y certbot python3-certbot-nginx
sudo ufw allow 'Nginx Full'
sudo ufw reload

The python3-certbot-nginx plugin is critical—it modifies your Nginx configuration automatically instead of requiring manual edits. Without it, you'd need to generate certificates separately and edit server blocks by hand, increasing error risk.

Obtaining your first certificate

Run Certbot with the Nginx plugin to handle both issuance and configuration in one step:

sudo certbot --nginx -d example.com -d www.example.com

Certbot will prompt for an email address (used for expiry warnings), agreement to terms, and optional EFF newsletter signup. After successful validation, it modifies your Nginx config to include SSL directives and redirects HTTP to HTTPS. Test the configuration before reloading:

sudo nginx -t
sudo systemctl reload nginx

A common mistake is skipping the syntax test. If Certbot's modifications conflict with existing custom directives, Nginx fails silently on reload, leaving your site down. Always validate.

How do you automate certificate renewal with Certbot?

Let's Encrypt certificates expire after 90 days. Manual renewal defeats the purpose of automation. Certbot installs a systemd timer during package installation, but you should verify it's active rather than assume:

sudo systemctl status certbot.timer
sudo certbot renew --dry-run

The dry-run test simulates renewal without hitting rate limits. If it succeeds, automatic renewal works. If it fails, check these common causes:

  • DNS changes: Domain no longer points to this server
  • Firewall rules: Port 80 blocked after initial setup
  • Nginx config errors: Syntax issues prevent reload post-renewal
  • Rate limits: Too many requests for the same domain set recently
Day 0IssuedRenewal Window(Days 60–90)Day 60Day 90ExpiresCertbot Auto-RenewBest Practice: Renew at Day 60+ to avoid edge-case failures near expiry
Certificate renewal timeline for HTTPS setup with Let's Encrypt and Certbot showing optimal renewal window

For production systems, I recommend adding a post-renewal hook to reload Nginx only when renewal actually occurs, avoiding unnecessary service interruptions:

sudo nano /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh
#!/bin/bash
systemctl reload nginx
echo "$(date): Nginx reloaded after cert renewal" >> /var/log/certbot-renewal.log
sudo chmod +x /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh

This approach integrates cleanly with monitoring. If you're building observability around certificate expiry, see the Prometheus metrics fundamentals guide for exporting cert expiry as a metric.

What security hardening steps follow HTTPS setup with Let's Encrypt and Certbot?

Obtaining a certificate is table stakes. Production deployments require additional hardening to achieve strong SSL Labs ratings and resist downgrade attacks. Edit your Nginx server block after Certbot completes its initial configuration:

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

    # Modern cipher suite (TLS 1.2+ only)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # Session caching reduces handshake overhead
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # HSTS prevents protocol downgrade attacks
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # OCSP stapling improves TLS performance
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 1.1.1.1 8.8.8.8 valid=300s;
    resolver_timeout 5s;

    root /var/www/html;
    index index.html;
}
Hardening DirectivePurposeRisk if Omitted
ssl_protocols TLSv1.2 TLSv1.3Disables vulnerable TLS 1.0/1.1POODLE, BEAST attacks possible
Strict-Transport-SecurityForces HTTPS for 2 yearsSSL stripping attacks on first visit
ssl_session_tickets offPrevents ticket key compromiseForward secrecy weakened
ssl_stapling onServer provides OCSP responseClient privacy leaked to CA
ssl_prefer_server_ciphers offTrusts client cipher preferenceIncompatible with modern clients

After applying these changes, test your configuration at SSL Labs. Aim for an A+ rating. If you receive warnings about missing HSTS preload eligibility or weak key exchange, revisit the cipher suite and header configuration. For broader server hardening beyond TLS, consult the Ubuntu security hardening guide.

How do you troubleshoot common Certbot failures?

Even straightforward HTTPS setup with Let's Encrypt and Certbot encounters issues. Here are the most frequent problems and their resolutions:

  1. "Unable to reach challenge URL": Verify DNS propagation with dig +short example.com. Confirm UFW allows port 80: sudo ufw status numbered. Check that no CDN or proxy intercepts the challenge path.
  2. "Too many certificates already issued": Let's Encrypt enforces rate limits (5 certs/week per domain set). Use --dry-run for testing. Wait 7 days or use a subdomain variation if blocked.
  3. "Plugin not found": You installed certbot without python3-certbot-nginx. Reinstall: sudo apt install python3-certbot-nginx.
  4. "Permission denied" on renewal: The systemd timer runs as root, but manual tests may fail if run without sudo. Always prefix with sudo for renewal commands.
  5. Nginx fails after renewal: Custom config conflicts with Certbot's managed block. Move custom directives outside the # managed by Certbot section or use include files.
Certbot FailsCheck Error Message TypeNetwork/DNS ErrorCheck UFW, dig, CDNRate Limit ErrorWait 7d or use --dry-runConfig/Plugin ErrorReinstall plugin, test nginxFix Network → RetryRespect Limits → RetryFix Config → RetryAlways validate: sudo nginx -t && sudo certbot renew --dry-run
Troubleshooting decision tree for HTTPS setup with Let's Encrypt and Certbot failures

Logging helps diagnose intermittent issues. Certbot writes to /var/log/letsencrypt/letsencrypt.log. For persistent problems, increase verbosity: sudo certbot renew --dry-run -v --debug-challenges. This outputs each validation step, revealing exactly where the process stalls.

Securing Your Production TLS Deployment

A successful HTTPS setup with Let's Encrypt and Certbot is just the foundation. Maintain security by monitoring certificate expiry through your observability stack, auditing cipher configurations quarterly as standards evolve, and testing renewals monthly in staging environments before production dependencies accumulate. If you manage multiple domains or need centralized secret storage for TLS artifacts across services, explore Kubernetes secrets management or HashiCorp Vault integration for larger fleets.

Need help implementing this on your infrastructure or auditing an existing TLS deployment? Get in touch for a consultation tailored to your environment.

Frequently Asked Questions

Yes, both are entirely free. Let's Encrypt is a nonprofit certificate authority providing zero-cost TLS certificates, and Certbot is an open-source ACME client maintained by the Electronic Frontier Foundation. There are no hidden fees for issuance or renewal in 2026.

Ninety days.

Run sudo apt update followed by sudo apt install certbot python3-certbot-nginx to install the latest stable Certbot package with Nginx plugin support from official Ubuntu repositories. Avoid pip installations unless you require specific plugin versions not available in system packages.

Yes, when using the nginx plugin with certbot --nginx, it modifies your server block to add SSL directives, redirect HTTP traffic, and specify certificate paths. Always review changes afterward since automatic edits may conflict with custom configurations or complex virtual host setups.

Not directly via HTTP validation. Use the dns-cloudflare plugin instead, which authenticates through Cloudflare API tokens rather than placing challenge files on your web server. This requires generating a restricted API token with Zone.DNS edit permissions in your Cloudflare dashboard.

Usually caused by firewall rules blocking port 80, incorrect document root paths, or CDN caching challenge responses. Verify your web server serves .well-known/acme-challenge correctly by testing manually before running Certbot again with verbose logging enabled.

Certbot installs a systemd timer automatically on most distributions. Verify with systemctl status certbot.timer and enable if disabled using sudo systemctl enable --now certbot.timer. The timer runs twice daily but only renews certificates within thirty days of expiration.

Renewal fails silently until the next scheduled attempt. Configure email notifications in /etc/letsencrypt/cli.ini or use monitoring tools to alert on failed renewals. Certificates remain valid until expiration, giving you time to restore service before browsers reject connections.

Yes, use the -d flag multiple times like certbot certonly -d example.com -d www.example.com -d api.example.com. All domains must resolve to the same server and pass validation. Wildcard certificates require DNS validation instead of HTTP challenges.

Run certbot revoke --cert-path /etc/letsencrypt/live/example.com/fullchain.pem immediately. This adds the certificate to OCSP revocation lists. Also regenerate private keys and request new certificates since revoked ones cannot be reinstated even after fixing security issues.

Yes.

RSA keys must be at least 2048 bits, though ECDSA P-256 or P-384 keys are now recommended for better performance and security. Certbot defaults to ECDSA in recent versions. Specify --key-type ecdsa explicitly if upgrading older RSA-based deployments for improved handshake speeds.

Install Certbot and obtain new certificates without downtime. Update your web server configuration to point to /etc/letsencrypt/live/domain/ paths, test with nginx -t or apachectl configtest, then reload. Cancel old certificate subscriptions afterward to prevent duplicate billing from commercial providers.

Yes, but HTTP validation requires the proxy to forward .well-known/acme-challenge requests to the backend server running Certbot. Alternatively, use standalone mode on a different port with proxy routing, or switch to DNS validation plugins that bypass HTTP entirely for cleaner architectures.

Fifty certificates per registered domain per week, five duplicate certificates per week, and ten failed validations per account per hour. Exceeding limits triggers temporary blocks lasting one week. Plan bulk migrations carefully and use staging environment first to avoid hitting production thresholds accidentally.