Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

HTTPS Setup with Let's Encrypt and Certbot

By Kokil Thapa | Last reviewed: August 2026

Getting a valid TLS certificate used to mean paying annual fees and waiting days for validation. Today, HTTPS setup with Let's Encrypt and Certbot is free, automated, and takes under five minutes on a properly configured server. This guide walks through the exact commands and configuration I use on production Ubuntu 24.04 systems serving Laravel applications and legal-tech portals in Nepal.

Before starting any certificate issuance, your server must have a working web root and correct DNS A/AAAA records pointing to its public IP. I always verify this first because Certbot will fail immediately if it cannot reach the domain over HTTP port 80. For developers building Laravel applications in Nepal or deploying client sites internationally, getting this foundation right prevents hours of debugging later. If you are managing multiple domains or need advanced infrastructure automation, reviewing DevOps best practices for website automation provides useful context for scaling beyond single-server setups.

How do you install Certbot on Ubuntu 24.04 for HTTPS setup with Let's Encrypt?

Ubuntu 24.04 LTS ships Certbot directly in the official repositories, which is now the recommended installation method for most deployments. The older PPA approach is no longer necessary unless you require specific pre-release features. On a fresh server, update your package index and install the appropriate plugin for your web server:

sudo apt update
sudo apt install certbot python3-certbot-nginx   # For Nginx
# OR
sudo apt install certbot python3-certbot-apache  # For Apache

This installs Certbot 2.x (the current stable branch in 2026) along with the Python dependencies and web server plugin. The plugin is critical: it allows Certbot to automatically modify your Nginx or Apache configuration to serve the ACME challenge files and install the certificate without manual editing.

Verifying the installation

After installation, confirm everything is working before attempting issuance:

  1. Check the version: certbot --version should return 2.x or higher.
  2. Verify the plugin loaded: certbot plugins lists installed authenticators and installers.
  3. Ensure your web server is running: systemctl status nginx or systemctl status apache2.
  4. Confirm port 80 is open: sudo ufw status must show "80/tcp ALLOW".

A common mistake on new Ubuntu 24.04 deployments is having UFW enabled but not explicitly allowing HTTP traffic. Certbot needs port 80 open even if you intend to serve only HTTPS, because the HTTP-01 challenge validation occurs over unencrypted HTTP initially.

apt installcertbot + plugincertbot pluginsverify authenticatorsystemctl statusweb server runningufw statusport 80 openCommon Failure: UFW blocks port 80 → certbot HTTP-01 challenge failsFix: sudo ufw allow 'Nginx Full' or sudo ufw allow 80/tcpSuccess: All checks pass → proceed to certificate issuanceReady for: certbot --nginx -d example.com
Certbot pre-flight verification checklist prevents common HTTPS setup failures on Ubuntu 24.04

How do you obtain and install SSL certificates using Certbot with Nginx or Apache?

With prerequisites verified, obtaining a certificate is a single command. Certbot handles domain validation, certificate issuance, and web server configuration automatically when using the appropriate plugin.

Nginx certificate installation

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

Certbot will prompt for an email address (used for expiry notifications), ask you to agree to the Let's Encrypt Terms of Service, and optionally offer to redirect all HTTP traffic to HTTPS. I always select the redirect option for production sites; there is rarely a valid reason to serve both protocols simultaneously in 2026.

The --nginx flag tells Certbot to parse your existing Nginx configuration, insert the SSL directives into the correct server block, and reload Nginx atomically. If your site uses non-standard paths or complex includes, Certbot may fail to detect the configuration. In that case, use standalone mode instead:

sudo certbot certonly --standalone -d example.com

Standalone mode temporarily binds to port 80 itself to complete validation. You must stop Nginx first (sudo systemctl stop nginx) or use the webroot method if stopping the server is unacceptable.

Apache certificate installation

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

The Apache plugin works identically to the Nginx plugin but modifies virtual host files in /etc/apache2/sites-available/. It enables the SSL module automatically if not already active and creates a new -le-ssl.conf file alongside your original configuration.

Webroot method for complex setups

For Laravel applications or custom deployments where the web server configuration is managed externally (e.g., via Deployer or Ansible), the webroot method gives you full control:

sudo certbot certonly --webroot -w /var/www/example.com/public -d example.com

This places challenge files in .well-known/acme-challenge/ within your document root without modifying server configuration. You then manually add the SSL directives to your Nginx or Apache config. This approach is preferable when you manage configurations as code and want Certbot to handle only certificate issuance.

--nginx / --apache✓ Auto-configures server✓ Zero downtime✓ Handles redirects✗ Fails on complex configs✗ Modifies files directlyBest for:Standard VPS deploymentsSingle-domain sitesQuick setup--standalone✓ Works without web server✓ No config parsing needed✗ Requires stopping server✗ Brief downtime✗ Manual SSL configBest for:Initial server setupNon-standard serversTroubleshooting--webroot✓ Zero downtime✓ Config as code friendly✓ Works with Deployer/CI✗ Manual SSL directives✗ Webroot must be accessibleBest for:Laravel / Symfony appsManaged deploymentsMulti-environment setups
Choosing the right Certbot method depends on your deployment workflow and server configuration complexity

How do you configure automatic SSL certificate renewal with Certbot?

Let's Encrypt certificates are valid for 90 days. Automatic renewal is mandatory for production systems; manually renewing every three months is unsustainable and error-prone. Certbot installs a systemd timer automatically during package installation on Ubuntu 24.04.

Verifying the renewal timer

sudo systemctl status certbot.timer

You should see "active (waiting)" with the next trigger time listed. The timer runs twice daily at randomized intervals to avoid hammering Let's Encrypt's servers. Certbot only attempts renewal when certificates are within 30 days of expiry, so most runs exit immediately.

Testing renewal safely

Always test renewal before relying on it in production:

sudo certbot renew --dry-run

The --dry-run flag simulates renewal against Let's Encrypt's staging environment without affecting live certificates. If this succeeds, automatic renewal will work. If it fails, the output shows exactly why—usually a DNS issue, firewall block, or web server misconfiguration introduced since initial issuance.

Post-renewal hooks

Some services need reloading after certificate renewal. For Nginx serving PHP-FPM applications, the web server reloads automatically via the plugin. But if you use HAProxy, Postfix, or other services consuming the same certificates, add a deploy hook:

sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh
#!/bin/bash
systemctl reload nginx
systemctl restart postfix
echo "$(date): Certificates renewed and services reloaded" >> /var/log/certbot-deploy.log
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh

Scripts in this directory execute after every successful renewal. This pattern is essential for securing websites and servers that integrate mail or proxy services alongside the primary web application.

Renewal MethodAutomation LevelDowntime RiskBest For
Systemd timer (default)Fully automaticNoneStandard Ubuntu 24.04 servers
Cron job (legacy)Scheduled manual commandLowOlder systems, non-systemd environments
Manual certbot renewHuman-triggeredModerateTroubleshooting, one-off renewals
Deploy hook scriptsAutomatic + post-renewal actionsNoneMulti-service deployments, Laravel + mail

What security headers and SSL hardening should accompany HTTPS setup with Let's Encrypt and Certbot?

A valid certificate alone does not make a site secure. Modern browsers and security scanners expect additional headers and TLS configuration. After completing HTTPS setup with Let's Encrypt and Certbot, add these protections to your Nginx server block:

# /etc/nginx/sites-available/example.com
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;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
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;

HSTS (HTTP Strict Transport Security) tells browsers to never connect via HTTP again for the specified duration. Start with a shorter max-age during testing, then increase to two years once confident. The preload directive submits your domain to browser-maintained HSTS lists, but this is irreversible—only enable it after thorough testing.

OCSP stapling

Enable OCSP stapling to improve TLS handshake performance and privacy:

ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;

Without stapling, browsers contact Let's Encrypt's OCSP responder separately during each new connection, adding latency and leaking browsing data. Stapling caches the OCSP response on your server and delivers it during the TLS handshake.

BrowserYour ServerLet's Encrypt OCSPClientHelloServerHello + Certificate+ OCSP Response (stapled)Finished → Encrypted DataWithout Stapling: Browser makes separate OCSP request → extra round trip + privacy leakWith Stapling: Server caches OCSP response → faster handshake, no third-party request
OCSP stapling eliminates extra browser requests during TLS handshake, improving performance and privacy

How do you troubleshoot common Certbot failures during HTTPS setup?

Even straightforward HTTPS setup with Let's Encrypt and Certbot encounters issues in production. These are the problems I encounter most frequently on client servers in Nepal and internationally.

DNS propagation delays

Certbot validates domain ownership by making HTTP requests to your domain. If you recently changed DNS records or added a new subdomain, propagation can take minutes to hours. Verify resolution before running Certbot:

dig +short example.com A
curl -I http://example.com/.well-known/acme-challenge/test

If dig returns no result or the wrong IP, wait. If curl fails but dig is correct, check your web server configuration and firewall rules.

Port 80 blocked by ISP or cloud provider

Some Nepali ISPs and certain cloud providers block inbound port 80 by default. This breaks HTTP-01 validation entirely. Test from an external network:

curl -v http://example.com  # From outside your network

If blocked, either request the provider to open port 80 or switch to DNS-01 validation, which does not require HTTP access at all. DNS-01 works by placing TXT records in your DNS zone and is ideal for internal services or restrictive networks.

Certificate rate limits

Let's Encrypt enforces rate limits to prevent abuse. The most commonly hit limit is 50 certificates per registered domain per week. During development or testing, use the staging environment:

sudo certbot --staging --nginx -d dev.example.com

Staging certificates are not trusted by browsers but bypass rate limits entirely. Switch to production only when your configuration is finalized.

Mixed content warnings after HTTPS migration

After enabling HTTPS, browsers may block insecure resources loaded over HTTP. For Laravel applications, ensure APP_URL in .env uses https:// and force scheme globally:

// app/Providers/AppServiceProvider.php
public function boot(): void
{
    if ($this->app->environment('production')) {
        URL::forceScheme('https');
    }
}

This ensures all generated URLs, including asset paths and form actions, use HTTPS. Without this, Blade templates and redirect responses may still reference HTTP, triggering mixed-content errors. For comprehensive technical SEO implications of HTTPS migrations, the technical SEO audit guide covers crawlability and indexation concerns specific to protocol changes.

Conclusion

Completing HTTPS setup with Let's Encrypt and Certbot on Ubuntu 24.04 is straightforward when you follow the correct sequence: verify prerequisites, choose the appropriate validation method, test renewal, and harden your TLS configuration. The entire process takes under ten minutes for standard deployments and eliminates the cost and complexity that once made HTTPS optional. In 2026, there is no excuse for serving unencrypted traffic—browsers penalize it, users distrust it, and search engines rank against it.

If you need help implementing HTTPS across multiple domains, integrating certificates into CI/CD pipelines, or auditing your existing TLS configuration for security gaps, get in touch to discuss your infrastructure requirements.

Frequently Asked Questions

Yes. Let's Encrypt is a non-profit certificate authority providing free DV SSL/TLS certificates. Certbot is the open-source client used to obtain and renew them. There are no licensing fees, hidden costs, or trial periods. You only pay for your server hosting. This makes HTTPS accessible for any Nepal-based business or personal project without annual certificate expenses.

Certificates are valid for 90 days. Certbot automatically renews them via systemd timer or cron, typically 30 days before expiry. Manual intervention is only needed if renewal fails due to DNS changes, blocked ports, or modified webroot paths. Always verify auto-renewal works after initial setup using certbot renew --dry-run to avoid unexpected expiration in production.

Any Linux server with Python 3, internet access, and port 80/443 open. Ubuntu 22.04/24.04 ships with compatible Python. Apache or Nginx must be running for HTTP validation. Root or sudo access is required. No special hardware is needed; even low-cost VPS instances in Kathmandu data centers handle this easily. Ensure your firewall allows inbound traffic on both ports during validation.

Run sudo apt update && sudo apt install certbot python3-certbot-apache. Then execute sudo certbot --apache -d yourdomain.com. Certbot modifies your Apache virtual host automatically, redirects HTTP to HTTPS, and configures auto-renewal. On production servers I maintain, I always verify the generated config and test with apachectl configtest before reloading. Never skip the dry-run test afterward to confirm renewal automation works correctly.

Yes. Install python3-certbot-nginx instead of the Apache plugin. Run sudo certbot --nginx -d yourdomain.com. Certbot edits your Nginx server block, adds SSL directives, and sets up redirects. In my experience deploying Laravel applications on Nginx, ensure your server_name directive matches exactly what you pass to Certbot. Mismatched names cause validation failures. Always reload Nginx after certificate issuance and verify with curl -I https://yourdomain.com.

Standalone mode spins up a temporary web server on port 80, requiring your existing web server to stop. Webroot mode places validation files in your live site’s .well-known directory without downtime. For production systems, always prefer webroot. On legal-tech portals I’ve built, stopping Apache even briefly risks dropped form submissions. Use standalone only during initial server provisioning when no web server is running yet.

Usually caused by firewall rules blocking port 80, incorrect DNS A records, or .well-known directory permission issues. Verify your domain resolves to the correct server IP. Check UFW status and ensure port 80 is open. Confirm your web server serves files from /.well-known/acme-challenge/ correctly. On one Nepal Gift Card deployment, Cloudflare proxying interfered with HTTP validation until we switched to DNS-01 challenge. Always inspect /var/log/letsencrypt/letsencrypt.log for specific error details.

Run sudo certbot certonly --expand -d existing.com -d newdomain.com. Certbot replaces the old certificate with a new one covering all specified domains. Do not create separate certificates unnecessarily; this hits rate limits faster. On multi-brand eCommerce sites like Petals Nepal, I consolidate subdomains under one cert. Remember that expansion requires re-validation for every domain listed. Test renewal immediately after expanding to prevent future automation failures.

Yes, but only via DNS-01 validation, not HTTP. You must use a Certbot DNS plugin matching your provider (e.g., python3-certbot-dns-cloudflare). Configure API credentials securely outside webroot. Wildcards cover *.example.com but not example.com itself—include both explicitly. On projects requiring many subdomains, this avoids managing dozens of individual certs. Note that DNS propagation delays can extend issuance time compared to standard HTTP validation.

Identical encryption strength. Let's Encrypt uses RSA-2048 or ECDSA P-256 keys, same as commercial CAs. The difference is validation level: DV only verifies domain control, not organization identity. For most web applications, including eCommerce and legal portals, DV is sufficient. Paid OV/EV certificates provide business verification displayed in browsers, but modern browsers no longer visually distinguish EV. Unless compliance mandates OV/EV, Let's Encrypt provides equivalent transport security at zero cost.

Your site shows browser security warnings after 90 days. Prevent this by configuring email alerts during certbot registration and monitoring /var/log/letsencrypt/letsencrypt.log. Set up external uptime checks that validate certificate expiry independently. On servers I manage, I add a cron job that runs certbot certificates weekly and emails results. Never assume auto-renewal works forever; DNS changes, expired API tokens, or OS upgrades break it. Proactive monitoring catches failures weeks before actual expiration.

Yes. Install Certbot alongside your current certificate. Obtain new Let's Encrypt certs without removing old ones first. Update your web server config to point to /etc/letsencrypt/live/yourdomain.com/ paths. Reload the web server and verify HTTPS works. Only then decommission the paid certificate. During migrations for client projects, I keep the paid cert as fallback for two weeks. This ensures zero downtime if Let's Encrypt validation encounters unexpected issues during transition.

Certbot operates at the web server level, independent of Laravel. Deployments via Deployer 7 don’t touch SSL certificates. However, ensure your deploy process doesn’t overwrite Certbot-managed Apache/Nginx configs. Store SSL paths in environment variables rather than hardcoding. On Laravel projects using shared release directories, certificate symlinks persist across deploys automatically. If you rebuild server configs during provisioning, include Certbot installation and initial cert acquisition in your bootstrap script to avoid post-deploy HTTPS gaps.

50 certificates per registered domain per week, 5 duplicate certificates per week, and 300 pending authorizations. Failed validations count toward limits. Exceeding triggers temporary blocks lasting one week. On multi-tenant platforms, plan certificate strategy carefully. Use SAN certificates to bundle domains instead of requesting individually. Monitor usage via crt.sh or Let's Encrypt’s rate limit dashboard. During staging/testing, always use --staging flag to avoid consuming production quota. Real production environments rarely hit limits with proper planning.

Recommended but optional. Add Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" to your Apache/Nginx config after confirming HTTPS works flawlessly. HSTS tells browsers to never attempt HTTP connections, preventing downgrade attacks. Enable only after thorough testing; misconfiguration locks out users if SSL breaks. On legal-tech portals handling sensitive documents, I always enable HSTS. Start with short max-age values during rollout, increase gradually. Include preload directive only if submitting to browser HSTS preload lists.

Share this article

Quick Contact Options
Choose how you want to connect me: