To install free SSL with Certbot today, you need no money, no call to support and no deep knowledge of cryptography: with Certbot and Let's Encrypt you get a valid SSL certificate for your domain in about 10 minutes, the padlock appears next to the address bar, and renewal is handed over to an automatic timer for good. This guide walks the whole path step by step: installing Certbot on Ubuntu, Debian and AlmaLinux, issuing the certificate with the Nginx and Apache plugins, the webroot and wildcard methods, backing up the certificate files, testing automatic renewal, the 301 redirect, and a table of the errors that usually leave the job half done. The commands were reviewed with Certbot 5.8 (September 2026) on Ubuntu 24.04 and AlmaLinux 9.
What does SSL/TLS actually do?
When a site runs on HTTPS, an encrypted channel using the TLS protocol is set up between the visitor's browser and your server. SSL is the name of the protocol's older generation; the name stuck, and the term "SSL certificate" comes from it. This channel provides three guarantees at once: confidentiality (no one along the path, whether the ISP or the café Wi-Fi, can read what is exchanged), integrity (no one can inject scripts or ads into your pages along the way) and authentication (visitors can be sure they are really talking to your server, not to a fake intermediary).
But it is not only about security; enabling HTTPS also has three practical, measurable consequences:
- User trust. Browsers label HTTP pages "Not Secure" and show an explicit warning on password forms served without HTTPS. For a shop, or any site with a login form, that warning means losing some of your visitors before their first click.
- A ranking signal. Google officially announced HTTPS as a ranking signal in 2014. Its weight is small, but between two otherwise equal results it tips the balance toward the secure one.
- HTTP/2 and modern features. Browsers implement HTTP/2 only over TLS, so an HTTP site effectively misses out on multiplexing and header compression. Service workers, PWAs, web push notifications and many other APIs also work only in a secure context.
Why Let's Encrypt is free, and why you can trust it
Let's Encrypt is a non-profit certificate authority (CA) that has operated since 2015 with financial backing from organizations such as Mozilla, Cisco, Chrome and the EFF, and today it is the world's largest CA by number of active certificates. Its root (ISRG Root X1) is trusted by default in every major browser and operating system. In other words, its SSL certificate is no different from a paid one in cryptography or browser trust, and visitors see no difference at all.
Being free is not a marketing trick either; it is the result of removing human labor from the issuance process entirely. Everything is automated through the standard ACME protocol (RFC 8555): an ACME client, the most popular being Certbot, built by the EFF, proves to the CA on your behalf that you control the domain, in one of two ways:
- The HTTP-01 challenge: the client places a small file under the site's
/.well-known/acme-challenge/path, and Let's Encrypt's servers fetch it over port 80. If the file is where it should be, control is proven. - The DNS-01 challenge: the client asks you to create a TXT record named
_acme-challenge. This is the only valid method for a wildcard certificate.
An important point for servers inside Iran, and for any server whose firewall filters by country: Let's Encrypt checks every challenge from several separate geographic locations (multi-perspective validation), and since September 15, 2025 every CA has been required to do so. So the server's port 80, or for DNS-01 the domain's DNS server, must also be reachable from outside the country; otherwise issuance fails with a During secondary validation error.
As for the 90-day lifetime of the certificates, it is not a weakness, contrary to first impressions. It was made short on purpose, so that if a server's private key leaks the window for misuse stays small and, more importantly, so that everyone gets used to automatic renewal. When a timer does the renewing, 90 days and 900 days make no difference.
To install free SSL with Certbot, first install Certbot on the server
This guide assumes a Linux server with root access and Nginx as the web server. If your web server is not up yet, first work through setting up a website on Ubuntu with Nginx and come back; the Certbot plugin builds directly on that configuration. If you are not yet comfortable with the server's terminal, read our guide to connecting to a server over SSH before you start; every command in this article is run in that terminal.
Ubuntu and Debian: the official method, snap
The Certbot team names the snap package as the recommended way to install, because you always get the latest version, the nginx and apache plugins are included, and automatic renewal is set up correctly from the start:
sudo apt update
sudo apt install snapd -y
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/local/bin/certbot
certbot --version
# certbot 5.8.0
If you have previously installed the repository version, remove it first with sudo apt remove certbot so that the two versions do not conflict.
The alternative route: apt
On servers that do not have snapd, or where you would rather not add it, the repository package works perfectly well; it is just usually a few versions behind. Ubuntu 24.04 ships version 2.9.0 in the universe repository, and it stays at that version for the life of this Ubuntu release:
sudo apt update
sudo apt install certbot python3-certbot-nginx -y
apt policy certbot
# certbot:
# Installed: 2.9.0-1
# Candidate: 2.9.0-1
AlmaLinux and Rocky: dnf and the EPEL repository
In the RHEL family the Certbot package lives in the EPEL repository, so enable that first. EPEL 9 currently provides version 3.1.0:
sudo dnf install epel-release -y
sudo dnf install certbot python3-certbot-nginx -y
certbot --version
# certbot 3.1.0
If your web server is Apache
Everything works as with Nginx; only the plugin name changes. The snap already includes it; with apt and dnf, install the python3-certbot-apache package, and write --apache instead of --nginx. The plugin creates the port 443 VirtualHost block by itself:
sudo apt install python3-certbot-apache -y # only for the apt route
sudo certbot --apache -d example.com -d www.example.com
Issuing an SSL certificate with the Nginx plugin
Before running the command, check two prerequisites. First, the domain's A record (and the www record, if you want www as well) must point to this server's IP; confirm it with dig +short example.com, and if you are not comfortable with records, see our plain-language guide to DNS records. Second, your Nginx configuration must contain a server block with the correct server_name, because the plugin uses that name to find and edit the right block.
sudo certbot --nginx -d example.com -d www.example.com
On the first run it asks for an email address for the ACME account (optional since version 3.3; press Enter to leave it empty) and asks you to accept the subscriber agreement. It then passes the HTTP-01 challenge by itself, and a few seconds later you see something like this:
Requesting a certificate for example.com and www.example.com
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/example.com/privkey.pem
This certificate expires on 2026-11-04.
Deploying certificate
Successfully deployed certificate for example.com to /etc/nginx/sites-enabled/example.com
Congratulations! You have successfully enabled HTTPS on https://example.com
After obtaining the certificate, the plugin edits your server block: it adds listening on port 443, sets the paths to the certificate and the private key, and includes secure TLS settings, all marked with a "managed by Certbot" comment so it is always clear which lines it wrote:
server {
server_name example.com www.example.com;
root /var/www/example.com;
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
--dry-run switch so the request goes to the test (staging) server and your real quota is not used up.The webroot method: when you don't want the service to stop or the config to be touched
The standalone method, common in older tutorials, listens on port 80 itself, so you have to stop the web server temporarily, which makes no sense on a live site. The webroot method passes the same HTTP-01 challenge through the running web server: Certbot places the challenge file inside the site's root, and Let's Encrypt fetches it like any other static file, without a second of downtime and without touching any configuration:
sudo certbot certonly --webroot -w /var/www/example.com -d example.com -d www.example.com
certonly means "just get the certificate and leave the configuration alone". In this mode you add the two lines ssl_certificate and ssl_certificate_key to the server block yourself and apply them with nginx -t && systemctl reload nginx. The webroot path is saved in the renewal file, so later renewals also run automatically and without downtime in the same way. Before running it, confirm with a test file that Nginx really serves from this root:
sudo mkdir -p /var/www/example.com/.well-known/acme-challenge
echo ok | sudo tee /var/www/example.com/.well-known/acme-challenge/test
curl -s http://example.com/.well-known/acme-challenge/test
# ok
Wildcard certificates: DNS-01 only
If you have many subdomains, or do not know them in advance, a wildcard certificate such as *.example.com covers them all at once. Let's Encrypt, however, accepts only the DNS-01 challenge for it, because control over the domain's entire DNS has to be proven:
sudo certbot certonly --manual --preferred-challenges dns \
-d example.com -d "*.example.com"
Certbot shows a text value that you must add as a TXT record named _acme-challenge in your DNS panel. Before pressing Enter, make sure the record has been published:
dig TXT _acme-challenge.example.com +short
# should print the exact value certbot asked for
An important point for users in Iran: if the domain's DNS is on a service that has an API plugin for Certbot (such as certbot-dns-cloudflare), creating the record and renewing are both automated. Most Iranian DNS panels, however, have neither an API nor a plugin, so this manual method (--manual) remains the only option, and it has no automatic renewal: you have to repeat it every cycle. The practical conclusion: do not get a wildcard until you have to. For a few known subdomains, put them all in one regular certificate with several -d flags; a single certificate holds up to 100 names.
Where are the certificate files stored, and how do I back them up?
Everything Certbot creates lives under /etc/letsencrypt, and Certbot itself will list it for you:
sudo certbot certificates
# Found the following certs:
# Certificate Name: example.com
# Serial Number: 4f3a9c1e0b7d2a8f6c5e4d3b2a1f0e9d8c7
# Key Type: ECDSA
# Domains: example.com www.example.com
# Expiry Date: 2026-11-04 07:15:21+00:00 (VALID: 89 days)
# Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
# Private Key Path: /etc/letsencrypt/live/example.com/privkey.pem
Three directories matter. live/example.com/ contains only symbolic links (symlinks) that always point to the latest version. The real files sit in archive/example.com/ with version numbers (cert1.pem, cert2.pem, …) that go up by one with each renewal. And renewal/example.com.conf is the renewal memory: the challenge type, the webroot path, the installer plugin and the key type are all read from this file. That is why you should always point the web server at the live/ path, so that a fresh certificate is served without any configuration change. Since version 2.0 the default key is ECDSA P-256; switch back with --key-type rsa only for very old clients.
live/ directory: the symlinks to archive/ arrive broken, and Nginx refuses to start with a "cannot load certificate" error. Move the whole of /etc/letsencrypt in one go with tar, or, more simply, get a fresh certificate on the new server. To make sure this directory is not left out of your nightly backups, see our Linux server backup guide.Automatic SSL renewal: a 90-day certificate that never expires
During installation Certbot creates a systemd timer that runs twice a day and renews every certificate that has reached the renewal threshold. Since version 4.0 that threshold is "less than one third of the lifetime remaining" (30 days for a 90-day certificate), and since version 4.1 renewal happens earlier if Let's Encrypt asks for it through ARI (ACME Renewal Information), for example when a certificate is about to be revoked. The apt (2.9) and EPEL (3.1) versions still follow the fixed 30-day rule. With the snap and with the Ubuntu/Debian apt package, this timer is active from the start; the EPEL package creates the timer file but does not start it, and warns you about this during installation (Certbot auto renewal timer is not started by default). On AlmaLinux and Rocky, be sure to run enable --now once:
systemctl list-timers | grep -i certbot
# snap.certbot.renew.timer -> snap install
# certbot.timer -> apt install (Ubuntu / Debian)
# certbot-renew.timer -> dnf / EPEL install (AlmaLinux / Rocky)
sudo systemctl enable --now certbot-renew.timer # AlmaLinux / Rocky only
Then simulate the whole renewal path today, not 89 days from now:
sudo certbot certificates # list certs and expiry dates
sudo certbot renew --dry-run
Congratulations, all simulated renewals succeeded:
/etc/letsencrypt/live/example.com/fullchain.pem (success)
If you obtained the certificate with the nginx plugin, Certbot reloads Nginx by itself after each renewal so that the fresh certificate is served. If you went the certonly and webroot route, hand this job to a hook; scripts in renewal-hooks/deploy run only after a successful renewal, not on every run of the timer:
printf '#!/bin/bash\nsystemctl reload nginx\n' \
| sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
And to be sure from the outside, ask the server itself for the expiry date of the certificate it is actually serving:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -dates
# notBefore=Aug 6 07:15:22 2026 GMT
# notAfter=Nov 4 07:15:21 2026 GMT
curl -vI https://example.com 2>&1 | grep "expire date"
# expire date: Nov 4 07:15:21 2026 GMT
openssl check into your periodic server monitoring and have it alert you, say, 15 days before expiry. Reaching 15 days means renewal has been failing for two weeks; the reason will be in /var/log/letsencrypt/letsencrypt.log.The 301 redirect and the final test
Since version 1.0, when Certbot installs a certificate with the nginx or apache plugin it also enables the HTTP-to-HTTPS redirect by default. If you used webroot, or simply want to make sure, the port 80 block should do just one thing:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
The 301 (permanent) code matters, not 302: it tells search engines that the HTTPS version is the main address and that the value of old links should pass to it. After the change, as always, run nginx -t && systemctl reload nginx. The renewal challenge does not break either: Let's Encrypt follows the redirect to HTTPS, so no exception is needed for /.well-known.
A common trap after the switch is mixed content: the page loads over HTTPS, but images and scripts are still requested from http:// addresses, and the browser either drops the padlock or blocks those resources. In WordPress, both the site address in the settings and the addresses stored in the database must be changed to https; we cover the right way to do it in our guide to installing and migrating WordPress on a VPS.
Finally, enter your domain in the free SSL Labs test. It takes a few minutes, but it gives you a complete report on the certificate chain, the protocols and any weak points. With the configuration Certbot creates, you usually get an A grade without any extra work.
Strict-Transport-Security) tells browsers to approach your domain only over HTTPS for months to come, and browsers do not forget that promise. Do not enable it until the redirect, automatic renewal and all your subdomains have worked without problems for a few weeks. If HTTPS breaks after you enable it, returning visitors cannot fall back to the HTTP version even temporarily, and the site is completely closed to them.Common SSL certificate errors and quick fixes
If issuance failed, or you saw strange behavior after installation, your problem is almost always one of these seven:
| Symptom / error message | Real cause | Fix |
|---|---|---|
Timeout during connect or Connection refused in the HTTP-01 challenge | Port 80 is closed in the firewall | Open ports 80 and 443 in ufw or firewalld (commands below) |
During secondary validation: … Timeout | Port 80 is open only from inside one country (for example, only inside Iran), so the validation points abroad cannot reach the server | Remove the firewall's geographic restriction, or switch to DNS-01 |
Invalid response from …/.well-known/acme-challenge/…: 404 | The -w path does not match the server block's real root, or the request lands in a different block | Find the right path with the curl test file above; check server_name |
no valid A records found or NXDOMAIN | The domain does not point to the server's IP yet, or DNS propagation has not finished | Create the A record, check it with dig +short and give propagation time |
too many certificates already issued | The limit of 5 certificates per week for the same set of names has been reached | Wait a few days (one slot frees up every 34 hours); always use --dry-run for testing |
CAA record prevents issuance | The domain's CAA record does not allow Let's Encrypt to issue | Fix the CAA record with the value 0 issue "letsencrypt.org" |
| A "too many redirects" error after installing the certificate | The site is behind Cloudflare and the SSL mode is still set to Flexible | Set Cloudflare's encryption mode to Full (strict) |
The most common of all is a closed port. The HTTP-01 challenge must reach port 80 of your server from the internet, and from several different countries. If you have locked the firewall down tightly following our Linux server security checklist, these two services need to be exceptions:
sudo ufw allow 'Nginx Full' # Ubuntu / Debian
sudo firewall-cmd --permanent --add-service=http # AlmaLinux / Rocky
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
The CAA case also has a one-line check: if the output is empty there is no problem, and if there is a record, letsencrypt.org must be allowed in it:
dig CAA example.com +short
# 0 issue "letsencrypt.org"
And then there is Cloudflare, which a large share of Iranian sites sit behind. In Flexible mode, Cloudflare talks HTTPS to the visitor but plain HTTP to your server; now that you have a 301 redirect in place, your server sends that same HTTP request back to HTTPS, and an endless loop is created: the famous "too many redirects". The right fix is not to remove the redirect. Now that you have a valid Certbot certificate on the origin server, set the mode to Full (strict) so that the path from the user to the server is encrypted and validated end to end. An SSL mismatch between Cloudflare and the origin server usually shows up as errors in the 52x family: 526 means Cloudflare did not accept the server's certificate, for example because it has expired or because only cert.pem is served instead of fullchain.pem. The full breakdown of this family is in our guide to fixing the 502 error.
Frequently asked questions
Is a free Let's Encrypt SSL certificate weaker than a paid one?
No: a free Let's Encrypt certificate gives exactly the same level of encryption, the same trust in browsers and the same padlock as a paid one. Paid certificates differ in the type of validation (organization validation, OV, and extended validation, EV), in issuance warranties and in support, none of which adds practical value for the vast majority of websites. That is why a large part of today's web runs on these same free certificates.
Why are Let's Encrypt certificates valid for only 90 days?
The short lifetime is deliberate: if a private key leaks, an attacker has only a short window to misuse it, and everyone is pushed to automate renewal. With the systemd timer that Certbot creates, renewal happens by itself about 30 days before expiry, and in practice you never notice it. The whole industry is moving the same way: the maximum lifetime of paid certificates dropped to 200 days in March 2026 and will reach 47 days by March 2029.
How can I be sure automatic SSL renewal really works?
Run three checks: systemctl list-timers should show the certbot timer as active; sudo certbot renew --dry-run should finish without errors; and check the real expiry date with openssl s_client or curl -vI. Since Let's Encrypt no longer sends expiry warning emails, put that last check into your periodic monitoring, so that a renewal that fails silently raises an alert before the certificate actually expires.
How many certificates do I need for several domains and subdomains?
Usually just one: a single certificate can hold up to 100 names, and you simply list them all with -d, one after another. If the number of subdomains is unknown or keeps changing, get a wildcard certificate, which requires the DNS-01 challenge. For completely unrelated domains, on the other hand, separate certificates are cleaner and safer.
How do I enable free SSL on cPanel or DirectAdmin shared hosting?
On shared hosting you need neither Certbot nor SSH, because the panel itself does the same job with Let's Encrypt. At Mehran Host, click the "Request certificate" button on the "SSL certificate" tab of the customer panel, and the certificate is issued and installed automatically; in cPanel and DirectAdmin, AutoSSL or the ACME Provider option does the same. The prerequisite is that the domain's A record points to that hosting account. The panel handles renewal itself; only the 301 redirect is left, which you put in .htaccess.
Next step
That is the whole path: install Certbot, get a certificate with the nginx plugin, test renew --dry-run on day one, and finally get your grade from SSL Labs. On Mehran Host's Iran web hosting, the "SSL certificate" tab of the customer panel does this job for you. If you want one complete, stress-free rehearsal before touching your main site, create an hourly cloud server at Mehran Host (provisioning takes about 60 seconds), point a test subdomain to it, go through the whole path from installation to breaking things and fixing them again, and pay only for those one or two hours of practice.