Let's Encrypt issues free SSL/TLS certificates for any domain, .ir and every other TLD included. The requirements are the same everywhere: the domain must point at your server, and Let's Encrypt's servers must be able to verify over the internet that you control it. On an Ubuntu server running Nginx, the whole job comes down to two commands: sudo apt install certbot python3-certbot-nginx and sudo certbot --nginx -d example.com -d www.example.com.

Let's Encrypt certificates are currently valid for 90 days, and the CA has announced plans to shorten that further. Getting the certificate is the easy part; automatic renewal is what matters. This guide walks through both, plus the difference between the HTTP-01 and DNS-01 challenges, wildcard certificates and the errors you are most likely to hit.

Short answer: Let's Encrypt gives a free SSL certificate to any domain, as long as the domain points at your server and Let's Encrypt can verify ownership from the internet. On Ubuntu with Nginx, install Certbot and its Nginx plugin, then run sudo certbot --nginx -d example.com -d www.example.com. Certificates last 90 days, and Certbot's systemd timer renews them automatically.

Before you start: three prerequisites

  1. DNS points at the server. The A record (and AAAA, if you have IPv6) for example.com and www.example.com must resolve to this server's IP. Check with dig example.com +short and dig www.example.com +short. If DNS records are new to you, see DNS records explained.
  2. Ports 80 and 443 are open. The HTTP-01 challenge always runs over port 80, even if the site will only ever serve HTTPS. With ufw:
bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status
  1. The web server is configured for the domain. The Nginx and Apache plugins look for a block whose server_name (or ServerName) matches the requested domain and install the certificate there.

If the server is brand new, the basics such as a firewall and a non-root user are covered in the first hour on a new Linux server.

Installing Certbot on Ubuntu 24.04

Certbot is the client Let's Encrypt recommends. The version in Ubuntu's repository is fine for most use cases:

bash
sudo apt update
# for Nginx
sudo apt install certbot python3-certbot-nginx
# or for Apache
sudo apt install certbot python3-certbot-apache

Getting a certificate with the Nginx plugin

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

The first run asks for an email address and acceptance of the terms of service. Certbot then completes the challenge, obtains the certificate, writes its paths into your Nginx config and reloads Nginx. To make the HTTP-to-HTTPS redirect behaviour explicit, add --redirect or --no-redirect; for most sites, redirecting to HTTPS is the right choice.

Every name the certificate should cover needs its own -d. A certificate issued only for example.com does not cover www.example.com, and browsers will show an error there.

With Apache

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

It behaves the same way: Certbot finds the matching VirtualHost, creates its SSL counterpart and reloads Apache.

Certificate only, without touching the config

If you would rather Certbot leave your web server files alone, use certonly with the webroot method. Certbot places the challenge file in the site's directory, and you reference the certificate in your config yourself:

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

Either way, certificates land in /etc/letsencrypt/live/example.com/: fullchain.pem is the certificate plus chain, and privkey.pem is the private key. Don't copy these files elsewhere; always reference this path, because renewals update the files in place.

HTTP-01 vs. DNS-01

Let's Encrypt asks you to prove control of the domain by completing a challenge. The two main ones:

HTTP-01 DNS-01
Method A file under /.well-known/acme-challenge/ served on port 80 A TXT record at _acme-challenge.example.com
Needs port 80 open Yes No
Wildcard certificate (*.example.com) Not possible The only option
Automatic renewal Simple Requires your DNS provider's API
Best for Most websites Wildcards, internal servers, no reachable port 80

The Nginx and Apache plugins and the webroot method all use HTTP-01. If you need a wildcard certificate, or the server isn't reachable from the internet on port 80, use DNS-01.

Manual DNS-01

bash
sudo certbot certonly --manual --preferred-challenges dns \
  -d 'example.com' -d '*.example.com'

Certbot shows you a value and waits while you create it as a TXT record at _acme-challenge.example.com. Before pressing Enter, confirm the record is visible:

bash
dig _acme-challenge.example.com TXT +short

The catch with the manual method is that it does not renew automatically: you have to repeat it every 90 days. For automated DNS-01, Certbot has plugins for popular DNS providers (for example python3-certbot-dns-cloudflare) that create and remove the record through an API token. If your DNS provider has no API, only get a wildcard if you genuinely need one; most sites do fine with a handful of explicit names.

Automatic renewal

On Ubuntu, the Certbot package installs a systemd timer that checks certificates twice a day and renews any that expire within 30 days. Confirm it exists:

bash
systemctl list-timers | grep certbot
systemctl status certbot.timer

Then run a test renewal. This uses Let's Encrypt's staging server and leaves your real certificate untouched:

bash
sudo certbot renew --dry-run

If that completes without errors, real renewals will work too. Run it again whenever you change your web server, firewall or DNS configuration.

With the Nginx or Apache plugin, the web server is reloaded automatically after renewal. With certonly, put a script to run after each successful renewal in /etc/letsencrypt/renewal-hooks/deploy/ and make it executable.

To list certificates and their expiry dates:

bash
sudo certbot certificates

Common errors and how to fix them

Port 80 is blocked

Messages such as Timeout during connect or Connection refused usually mean Let's Encrypt couldn't reach port 80. Check the server firewall, your hosting provider's cloud firewall (separate from ufw), and that the web server is actually listening on port 80:

bash
sudo ss -tlnp | grep ':80 '

Let's Encrypt validates the challenge from several vantage points on the internet, so the server must be reachable from outside its own network. Loading the site from inside that network proves nothing.

DNS doesn't point at the server yet

If you just created or changed the A record, resolvers may still be caching the old answer. The error message usually shows the IP Let's Encrypt reached; if it's the old server, wait for the TTL to expire. A stale AAAA record is another common culprit: when Let's Encrypt sees an AAAA record, it prefers IPv6.

Rate limits

Let's Encrypt enforces rate limits to prevent abuse. The two you're most likely to hit are a limit on duplicate certificates (exactly the same set of names) per week, and a limit on failed validations per hour. The exact numbers are on the Let's Encrypt Rate Limits page.

Prevention is simple: use the staging server while you're testing your setup. Its certificates aren't trusted by browsers, but its limits are far higher:

bash
sudo certbot --nginx --test-cert -d example.com -d www.example.com

Once everything works, run the same command without --test-cert.

CAA record

If the domain has a CAA record that doesn't list letsencrypt.org, issuance is refused. Either add letsencrypt.org to the record or remove the CAA record.

Checking the expiry date with openssl

certbot certificates shows what's on disk; openssl shows what the site actually serves to visitors. The two aren't always the same, for example when a renewal succeeded but the web server was never reloaded:

bash
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

notAfter is the expiry date. For a simple check in a script or cron job, -checkend returns a non-zero exit code if the certificate expires within n seconds:

bash
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -checkend 1209600 \
  || echo "certificate for example.com expires within 14 days"

Since 2025, Let's Encrypt no longer sends expiry reminder emails, so if you have no monitoring in place, these few lines are worth adding to cron. For scheduling, see cron jobs in Linux.

Frequently asked questions

How long is a Let's Encrypt certificate valid?

Let's Encrypt certificates are currently valid for 90 days. On Ubuntu, the Certbot package installs a systemd timer that checks twice a day and renews any certificate with less than 30 days left.

Is a free Let's Encrypt certificate less secure than a paid one?

No. The encryption is just as strong as with a paid certificate, and every major browser trusts Let's Encrypt. The difference is that Let's Encrypt only validates domain ownership and does not issue certificates that also verify an organisation's identity.

How do I get a free wildcard SSL certificate?

A wildcard certificate such as *.example.com can only be issued through the DNS-01 challenge, which means creating a TXT record at the domain's _acme-challenge name. The manual method has to be repeated every 90 days, so for automatic renewal use the Certbot plugin for your DNS provider, which creates the record through an API token.

Why does Certbot fail with Timeout during connect?

It means Let's Encrypt's servers couldn't reach port 80 on your server. Check the server firewall, your provider's cloud firewall and that the web server listens on port 80, and make sure the domain's A or AAAA record really points at this server.

How do I check that Certbot auto-renewal is working?

Run sudo certbot renew --dry-run, which simulates a renewal against Let's Encrypt's staging server without touching your real certificate. Also confirm the timer is active with systemctl status certbot.timer.

Wrap-up

A free SSL certificate for a .ir domain works exactly like one for any other domain: point DNS at the server, open port 80, and run certbot --nginx or certbot --apache with every name you need. For wildcards, use DNS-01 and automate it with your DNS provider's API plugin if you can. After installing, run certbot renew --dry-run, confirm the timer is active, and keep a simple openssl expiry check in place. While you're still testing, --test-cert keeps you clear of the rate limits.