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.

Install SSL Certificates on Ubuntu

By Kokil Thapa | Last reviewed: September 2026

Every production site needs HTTPS, and the fastest path on a fresh VPS is to install SSL certificates on Ubuntu with Certbot and Let's Encrypt. I've done this on dozens of Ubuntu server setups for Laravel apps, WordPress shops, and legal-tech portals. The process is straightforward when DNS, the web server, and port 443 are aligned. This guide walks through the full workflow from prerequisites to auto-renewal and the errors you will actually hit in production.

What do you need before you install SSL certificates on Ubuntu?

Let's Encrypt validates that you control the domain. It does not care about your app stack. It cares about DNS, HTTP reachability, and an open port 443 after issuance.

Start with these checks before you run Certbot.

  1. Registered domain — You need an A or AAAA record pointing to your server's public IP. Use your registrar panel or follow our Ubuntu DNS configuration guide if records are unclear.
  2. Working web server — Apache or Nginx must serve the domain on port 80. If you have not installed one yet, read how to install Nginx on Ubuntu or set up Apache first.
  3. Firewall rules — Ports 80 and 443 must be open. On UFW: sudo ufw allow 'Nginx Full' or the Apache equivalent.
  4. Correct server block — The virtual host must match the exact hostname you request in the certificate. www and bare domain are different names unless you request both.
  5. Updated packages — Run sudo apt update && sudo apt upgrade -y before installing Certbot. Stale OpenSSL builds cause odd handshake failures.

On shared EC2 boxes where I run multiple law-firm sites, I always confirm DNS propagation with dig +short example.com A from the server itself. A mismatch here wastes twenty minutes of debugging later.

Install SSL Certificates on Ubuntu — HTTPS FlowBrowserHTTPS :443UFWPorts 80/443Nginx/ApacheTLS terminatePHP AppLaravel/WPTLS Handshake (simplified)1. ClientHellocipher list2. ServerHellocert chain3. Exchangesession keys4. EncryptedHTTP/2 dataCert files live in /etc/letsencrypt/live/yourdomain/
Install SSL certificates on Ubuntu — browser to app flow with TLS handshake stages

Commercial certificates from DigiCert or Sectigo still have a place for EV badges or strict compliance policies. For most Laravel, Symfony, and WordPress deployments I maintain, free Let's Encrypt certs are the right default. They auto-renew and browsers trust them equally for standard padlock display.

How do you install SSL certificates on Ubuntu with Certbot?

Certbot is the official client maintained by the Electronic Frontier Foundation. It talks to Let's Encrypt, writes certificate files, and can patch your web server config in one pass. Install it from the Ubuntu repositories or the Certbot PPA on older releases.

Install Certbot on Ubuntu 22.04 or 24.04

sudo apt update
sudo apt install certbot python3-certbot-nginx -y
# For Apache instead:
sudo apt install certbot python3-certbot-apache -y

The plugin name matches your web server. Do not install both plugins unless you actually run both servers on the same box — that is rare and confusing.

Obtain a certificate with the Nginx plugin

Ensure your Nginx server block already listens on port 80 and defines server_name correctly. Then run:

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

Certbot performs an HTTP-01 challenge. It places a token file where Let's Encrypt can fetch it over port 80. On success, it writes files under /etc/letsencrypt/live/example.com/ and adds a listen 443 ssl block with certificate paths.

Obtain a certificate with the Apache plugin

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

Apache sites must be enabled with a2ensite before Certbot runs. The plugin creates or updates the SSL virtual host and enables mod_ssl if needed. Pair this with our guide on installing PHP on Ubuntu when you run PHP-FPM behind Apache.

Standalone and webroot modes

Use standalone mode only when no web server occupies port 80:

sudo certbot certonly --standalone -d example.com

Webroot mode suits Docker or reverse-proxy setups where the app serves files from a known directory:

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

After webroot issuance, you manually point Nginx or Apache at the certificate paths. That gives you full control but adds config work the plugins skip.

Certbot Workflow on UbuntuInstallapt certbotChallengeHTTP-01 :80Issue Cert90-day LEDeploy443 sslAuto-Renewal (systemd timer)Timer firestwice dailycertbot renewdry-run okReload webnginx/apacheRenew when cert has 30 days or less remaining
Certbot issuance path and systemd auto-renewal after you install SSL certificates on Ubuntu

Official reference: the Certbot installation instructions at EFF list distro-specific commands. Ubuntu packages track stable Certbot releases and work well on 22.04 LTS and 24.04 LTS servers I manage today.

How do you configure Apache or Nginx after obtaining an SSL certificate?

When you use certbot --nginx or certbot --apache, most config is automatic. Manual setups — common on multi-app VPS boxes — need explicit SSL directives.

Nginx SSL server block

server {
    listen 443 ssl http2;
    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;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    root /var/www/example.com/public;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
}

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

The HTTP-to-HTTPS redirect is not optional for public sites. Search engines and browsers treat HTTP URLs as a separate origin. Your canonical tags and sitemaps should already point to HTTPS — a topic covered in our technical SEO service notes.

Apache SSL virtual host

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/public

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>

Enable headers module and add HSTS once HTTPS is confirmed working:

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

Test config before reload. Nginx: sudo nginx -t. Apache: sudo apachectl configtest. A syntax error during reload takes the site offline — I always test first, then sudo systemctl reload nginx.

Certificate file reference

  • fullchain.pem — leaf cert plus intermediate chain; use this in ssl_certificate.
  • privkey.pem — private key; permissions must stay root-readable only.
  • cert.pem — leaf cert alone; rarely referenced directly.
  • chain.pem — intermediate certs without the leaf.

Never commit privkey.pem to Git. On Deployer-based pipelines I use Certbot on the server and keep keys out of the release artifact entirely. See Symfony deployment on Ubuntu VPS for a similar pattern with PHP apps.

MethodCostSetup timeAuto-renewalBest for
Certbot + Let's EncryptFree5–15 minYes (systemd)Laravel, WordPress, most VPS sites
Manual CSR + commercial CARs 5,000–25,000/yr (~USD 37–185)1–3 daysManualEV certs, legacy compliance
Cloudflare Origin certFree10 min15-year cert, manual rotationSites proxied through Cloudflare
Self-signedFree2 minNoLocal dev and internal tools only
SSL Method Picker for UbuntuPublic production site?Yes → Certbot + Let's EncryptBehind Cloudflare proxy?Origin cert or Full strict LEMulti-domain VPS?One cert per vhost or SAN certLocal dev only?Self-signed — never publicRecommended: Certbot on Ubuntu 22/24 LTSFree, trusted, auto-renews via systemd timer
Decision tree for choosing an SSL approach when you install SSL certificates on Ubuntu

How do you renew SSL certificates automatically on Ubuntu?

Let's Encrypt certificates expire after 90 days. Certbot installs a systemd timer that attempts renewal twice per day. You still need a deploy hook that reloads the web server after a successful renew.

Verify the timer is active

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

The dry run must finish without errors. If it fails, fix the root cause before the real cert expires. Expired certs trigger browser warnings and can break payment gateway callbacks overnight.

Add a renew hook for Nginx

sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
#!/bin/bash
systemctl reload nginx
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Apache users replace the reload command with systemctl reload apache2. Schedule this alongside other maintenance in your Ubuntu cron jobs guide workflow — backup scripts and log rotation should not collide with renewal windows.

Rate limits to know

Let's Encrypt allows roughly 50 certificates per registered domain per week. Staging environment helps during testing:

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

Hitting production rate limits during a misconfigured loop is painful. I use staging until DNS and vhost config are confirmed on new domains like those on Notary Nepal and sister legal portals.

Ubuntu's OpenSSL package ships with the distro. For cipher guidance, Mozilla publishes current recommended settings at their SSL Configuration Generator. Certbot's options-ssl-nginx.conf already follows sensible defaults — avoid weakening them to support ancient clients unless you have a documented business reason.

How do you troubleshoot SSL certificate problems on Ubuntu?

Most SSL failures fall into five buckets. Work through them in order instead of reinstalling Certbot repeatedly.

1. DNS not pointing to this server

Symptom: Connection refused or challenge timeout. Fix A/AAAA records, wait for TTL, then retry. Use host example.com from the server to confirm the IP matches curl ifconfig.me.

2. Port 80 blocked or hijacked

HTTP-01 needs port 80 reachable from the public internet. Cloudflare orange-cloud proxy must be temporarily disabled or switched to DNS-only during initial issuance unless you use DNS-01 with a Cloudflare API plugin. Check UFW and your cloud security group.

3. Wrong server_name or missing vhost

Certbot binds to the vhost matching your -d flag. A default site catching all traffic causes mismatched challenges. Disable the default Nginx site if it conflicts: sudo rm /etc/nginx/sites-enabled/default.

4. Mixed content after HTTPS works

The padlock breaks when HTML loads over HTTPS but assets load over HTTP. Fix hard-coded http:// URLs in WordPress with wp search-replace or Better Search Replace. Laravel apps should set APP_URL=https://example.com and use asset() helpers. Our speed optimization work often surfaces mixed-content issues during Core Web Vitals audits.

5. Expired cert or failed renewal

Check expiry: sudo certbot certificates. Force renew: sudo certbot renew --force-renewal. Then reload the web server. Add monitoring — even a weekly cron check that emails when expiry is under 14 days saves a Friday-night outage.

SSL Troubleshooting on UbuntuCert errorbrowser warnCheck DNSdig / hostCheck :80UFW + vhostCheck :443ss -tlnpDiagnostic Commandsopenssl s_client -connect example.com:443sudo certbot certificatessudo nginx -t && sudo certbot renew --dry-runcurl -I https://example.comFix applied?Reload web serverClear CDN cache
Troubleshooting flow after you install SSL certificates on Ubuntu — DNS, ports, and diagnostic commands

Harder production setups — load balancers, multiple PHP versions, or MySQL on a separate host — still terminate TLS on the edge Nginx box. Keep certificate management on the public-facing node only. Internal traffic between app and database servers stays on a private network without public certs.

Security hardening does not stop at HTTPS. Pair TLS with fail2ban on Ubuntu, regular security updates, and the broader checklist in our server hardening guide. For managed hosting where SSL, DNS, and renewals are handled for you, see domain registration and hosting in Nepal.

When validating JSON API responses over HTTPS during integration work, a quick pass through the JSON formatter tool catches malformed payloads before they hit production clients.

Key Takeaways

  • Point DNS to your server and open ports 80 and 443 before you install SSL certificates on Ubuntu.
  • Use certbot --nginx or certbot --apache for the fastest path; webroot mode suits custom stacks.
  • Always add an HTTP-to-HTTPS redirect and verify with certbot renew --dry-run.
  • Store private keys only on the server — never in Git repos or deployment artifacts.
  • Troubleshoot in order: DNS, port 80 challenge, vhost name match, then mixed content.
  • Pair TLS with firewall rules, fail2ban, and monitoring so renewals never surprise you.

People Also Ask

Is Let's Encrypt free for commercial websites?

Yes. Let's Encrypt issues domain-validated certificates at no cost for any site, commercial or personal. Browsers trust them the same as paid DV certificates. Extended Validation (EV) badges require a paid commercial CA instead.

How long does an SSL certificate from Let's Encrypt last?

Let's Encrypt certificates are valid for 90 days. Certbot's systemd timer renews them automatically when 30 days or fewer remain. Shorter lifetimes reduce the damage window if a key is compromised.

Can I install SSL on Ubuntu without a domain name?

Let's Encrypt requires a publicly resolvable domain or subdomain. IP-only certificates are not issued. For local development, use a self-signed cert or tools like mkcert — never expose self-signed certs on public production URLs.

Does installing SSL slow down my Ubuntu server?

Modern TLS with HTTP/2 has negligible overhead on current hardware. Session resumption and OCSP stapling keep handshakes fast. The performance gain from HTTP/2 multiplexing often outweighs the small CPU cost of encryption.

Ship HTTPS the right way on your next Ubuntu deploy

Install SSL certificates on Ubuntu once, automate renewal, and HTTPS becomes invisible infrastructure instead of a recurring fire drill. The commands above cover every Laravel, WordPress, and Symfony site I deploy on Ubuntu 22.04 and 24.04 today. If you want TLS, DNS, and server hardening handled end to end, Linux system administration support or a full review through ongoing maintenance may fit your team better than doing it alone. Contact us with your domain and stack — or browse the Court Marriage in Nepal and Adventure Third Pole Trek projects for examples of production HTTPS sites on shared Ubuntu infrastructure.

Frequently Asked Questions

Let's Encrypt validates domain control, not your application stack. Before running Certbot, confirm your domain has an A or AAAA record pointing to the server's public IP, Apache or Nginx is serving that hostname on port 80, UFW or your cloud security group allows ports 80 and 443, and the virtual host server_name matches the exact domain you request. Run sudo apt update and sudo apt upgrade first. On shared VPS boxes I manage, I verify DNS from the server itself with dig +short example.com A before touching Certbot — a mismatch wastes twenty minutes of debugging.

Certbot is the official Let's Encrypt client. On Ubuntu 22.04 or 24.04, install it with sudo apt install certbot plus the plugin matching your web server: python3-certbot-nginx or python3-certbot-apache. Ensure your server block listens on port 80 with the correct server_name, then run sudo certbot --nginx -d example.com -d www.example.com or the Apache equivalent. Certbot performs an HTTP-01 challenge over port 80, writes certificate files under /etc/letsencrypt/live/example.com/, and patches your web server config to listen on port 443 with the correct certificate paths.

Yes. Let's Encrypt issues domain-validated certificates at no cost for any site, commercial or personal. Browsers display the same padlock as paid DV certificates.

Let's Encrypt certificates are valid for 90 days. Certbot's systemd timer attempts renewal twice daily, typically renewing when 30 days or fewer remain.

No. Let's Encrypt requires a publicly resolvable domain or subdomain and does not issue certificates for IP addresses alone. Use self-signed certs for local development only.

Both commands obtain the same Let's Encrypt certificate but use different plugins to read and modify your web server configuration. Use certbot --nginx when Nginx terminates HTTP on port 80, and certbot --apache when Apache does. Each plugin creates or updates the SSL virtual host, sets certificate paths, and enables SSL modules as needed. Install only the plugin you actually use — running both on one server is rare and confusing. Apache sites must be enabled with a2ensite before Certbot runs so the plugin can find the matching vhost.

Use standalone mode only when nothing else occupies port 80: sudo certbot certonly --standalone -d example.com. Certbot temporarily binds to port 80 itself for the HTTP-01 challenge. Webroot mode suits Docker containers, reverse-proxy setups, or multi-app VPS boxes where you know the document root: sudo certbot certonly --webroot -w /var/www/example.com/public -d example.com. After webroot issuance you manually point Nginx or Apache at fullchain.pem and privkey.pem under /etc/letsencrypt/live/. That gives full config control but skips the automatic server patching the nginx and apache plugins provide.

Point ssl_certificate or SSLCertificateFile at /etc/letsencrypt/live/example.com/fullchain.pem and the key at privkey.pem. Nginx should include /etc/letsencrypt/options-ssl-nginx.conf and ssl_dhparam from /etc/letsencrypt/ssl-dhparams.pem. Apache includes /etc/letsencrypt/options-ssl-apache.conf. Add a separate port 80 server block that returns 301 to HTTPS. Test syntax with sudo nginx -t or sudo apachectl configtest before reload — a bad config takes the site offline. Once HTTPS is confirmed working, enable HSTS via the headers module with max-age=31536000 and includeSubDomains.

Certbot installs a systemd timer that attempts renewal twice per day. Verify it with sudo systemctl status certbot.timer and run sudo certbot renew --dry-run — this must finish without errors before you rely on it in production. Expired certificates trigger browser warnings and can break payment gateway callbacks overnight. Add a deploy hook at /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh that runs systemctl reload nginx after a successful renew. Apache users substitute systemctl reload apache2. Without the reload step, renewed certificate files exist on disk but the running web server still serves the old cert from memory.

Certbot with Let's Encrypt costs nothing and auto-renews via systemd — that is the default I use on Laravel, WordPress, and Symfony sites on Ubuntu 22.04 and 24.04. Commercial certificates from DigiCert or Sectigo run roughly Rs 5,000–25,000 per year (~USD 37–185) and suit Extended Validation badges or strict compliance policies requiring a commercial CA. Cloudflare Origin certificates are free for sites proxied through Cloudflare but require manual rotation every 15 years. Self-signed certificates cost nothing but browsers reject them on public URLs — reserve those for local development only.

For most production Laravel, Symfony, and WordPress deployments I maintain, free Let's Encrypt certificates are the right default. Browsers trust them equally for standard padlock display, and Certbot handles renewal automatically. Choose a paid commercial CA when you need an Extended Validation badge visible in the browser chrome, must satisfy a compliance policy that explicitly requires a specific certificate vendor, or operate legacy systems that cannot handle Let's Encrypt's 90-day rotation cycle. Cloudflare Origin certificates work when your site is already proxied through Cloudflare and you terminate TLS on the origin server with a long-lived cert you rotate manually.

HTTP-01 validation requires Let's Encrypt to reach your server on port 80 from the public internet. Work through causes in order: confirm DNS A or AAAA records point to this server's IP using host example.com and compare against curl ifconfig.me, ensure UFW and your cloud security group allow ports 80 and 443, and verify no other process hijacks port 80. If Cloudflare orange-cloud proxy is enabled, switch to DNS-only during initial issuance unless you configure DNS-01 with a Cloudflare API plugin. A default Nginx site catching all traffic also causes challenge mismatches — disable it if it conflicts with your domain's server_name.

The certificate itself may be valid while the page still loads insecure assets. Mixed content occurs when HTML is served over HTTPS but images, scripts, or stylesheets load over hard-coded http:// URLs. Fix WordPress sites with wp search-replace or the Better Search Replace plugin. Laravel applications should set APP_URL=https://example.com and use asset() helpers instead of hard-coded URLs. I encounter this regularly during Core Web Vitals audits — the TLS handshake succeeds but the browser withholds the secure padlock until every subresource loads over HTTPS. Check browser developer tools Network tab to identify the offending HTTP URLs.

Yes, for public production sites the HTTP-to-HTTPS redirect is not optional. Search engines and browsers treat HTTP and HTTPS URLs as separate origins, so without a 301 redirect you split indexation and confuse canonical tags and sitemaps that should already point to HTTPS. Add a port 80 server block in Nginx that returns 301 https://$host$request_uri, or the Apache equivalent. Test the redirect in a browser and with curl -I http://example.com before considering the SSL installation complete. Pair the redirect with HSTS headers once you confirm HTTPS works end to end across all pages and subresources.

Check current certificates with sudo certbot certificates and note the expiry date. Force an immediate renewal with sudo certbot renew --force-renewal, then reload the web server with sudo systemctl reload nginx or sudo systemctl reload apache2. If renewal fails, run sudo certbot renew --dry-run to surface the root cause — usually DNS drift, a blocked port 80, or a renamed virtual host. Add monitoring so you get alerted when expiry falls under 14 days; a weekly cron email check has saved me from Friday-night outages on production legal-tech portals. Never commit privkey.pem to Git or deployment artifacts — keys stay on the server only.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: