Previous Article
Facebook Social Plugins in 2026: What Still Works and What Needs an App
22 September 2018
HTTPS is no longer optional. Browsers mark plain HTTP as "Not secure", search engines rank it lower, and modern browser APIs — geolocation, service workers, the clipboard — simply refuse to run without it. Installing a certificate is the mechanism that turns it on.
This guide covers the way you should almost always do it now (Let's Encrypt, free and automatic), the manual process for a purchased certificate, and a self-signed certificate for local development.
A terminology note that matters, because the old names cause real confusion. "SSL" refers to a protocol whose last version, SSL 3.0, was broken by the POODLE attack in 2014 and is disabled everywhere. Its replacement is TLS, currently TLS 1.3.
Everyone still says "SSL certificate" out of habit, but the certificate itself is protocol-agnostic — the same .crt works with TLS. When you configure a server, make sure it offers only TLS 1.2 and 1.3 and nothing older.
.crt.For any public-facing site, this is what you want. Let's Encrypt issues free, browser-trusted certificates, and Certbot installs and renews them for you.
# Debian / Ubuntu sudo apt install certbot python3-certbot-apache # Issue and install in one step — Certbot edits your Apache config for you sudo certbot --apache -d example.com -d www.example.com
Certbot asks for an email (for expiry warnings), obtains the certificate, writes the HTTPS virtual host, and offers to redirect HTTP to HTTPS. Say yes to the redirect.
Let's Encrypt certificates last 90 days. That short window is deliberate — it forces automation, because a manual 90-day chore is one you will eventually miss. Certbot installs a renewal timer automatically. Confirm it works:
sudo certbot renew --dry-run
If that succeeds, you never think about certificates again. This alone is the reason to prefer Let's Encrypt over a purchased certificate for most sites.
If you bought a certificate — for an extended-validation cert, or because a client requires a specific CA — you install the files by hand. You will have received:
your_domain.crt — your certificateyour_domain.key — the private key you generated with the CSRintermediate.crt or a "CA bundle" — the intermediate chain# 2048-bit RSA is the minimum; 4096 or an ECDSA key is stronger openssl req -new -newkey rsa:2048 -nodes \ -keyout example.com.key \ -out example.com.csr
You will be prompted for details. The one that must be exact is Common Name — it has to match your domain precisely (www.example.com is different from example.com; use a SAN or wildcard cert if you need both).
Guard the .key file. It is the private key — anyone who has it can impersonate your site. Never email it, never commit it, and set its permissions to 600.
Send only the .csr to the certificate authority. Keep the .key.
sudo mkdir -p /etc/ssl/example.com sudo cp example.com.crt /etc/ssl/example.com/ sudo cp intermediate.crt /etc/ssl/example.com/ sudo cp example.com.key /etc/ssl/example.com/ sudo chmod 600 /etc/ssl/example.com/example.com.key
<VirtualHost *:443>
ServerName example.com
DocumentRoot /var/www/example.com/public
SSLEngine on
SSLCertificateFile /etc/ssl/example.com/example.com.crt
SSLCertificateKeyFile /etc/ssl/example.com/example.com.key
SSLCertificateChainFile /etc/ssl/example.com/intermediate.crt
# Offer only modern protocols. No SSLv3, no TLS 1.0/1.1.
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder off
</VirtualHost>
SSLCertificateChainFile is the line most manual installs miss. Without the intermediate, your certificate installs "successfully" and then some browsers — particularly on Android and older devices — refuse to connect because they cannot build the chain to a trusted root. On newer Apache (2.4.8+) you can instead concatenate your cert and the intermediate into one file and point SSLCertificateFile at it; order matters, your cert first.
Enable the module and test before reloading:
sudo a2enmod ssl sudo apache2ctl configtest # "Syntax OK" before you reload sudo systemctl reload apache2
Installing a certificate does not stop people reaching the insecure version. Force the redirect:
<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>
Then tell browsers to skip HTTP entirely on future visits with HSTS — but only once you are confident HTTPS is solid, because it is hard to undo:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
For localhost you do not need a real CA — a self-signed certificate is fine, with the understanding that browsers will warn about it because no authority vouches for it.
openssl req -x509 -newkey rsa:2048 -nodes \ -keyout localhost.key -out localhost.crt \ -days 365 -subj "/CN=localhost"
Better, use mkcert, which creates a locally-trusted certificate so your browser shows no warning:
mkcert -install mkcert localhost 127.0.0.1
Never use a self-signed certificate in production. Visitors get a full-page security warning, and training people to click through those is its own harm. Let's Encrypt is free — there is no reason to.
# From the command line — shows the chain and expiry openssl s_client -connect example.com:443 -servername example.com # Check the expiry date directly echo | openssl s_client -connect example.com:443 2>/dev/null \ | openssl x509 -noout -dates
For a thorough external check, run the free SSL Labs test. Aim for an A. It flags a missing intermediate, weak protocols still enabled, and imminent expiry — the three most common real problems.
| Symptom | Cause |
|---|---|
| NET::ERR_CERT_AUTHORITY_INVALID | Self-signed, or missing intermediate chain |
| ERR_CERT_COMMON_NAME_INVALID | Certificate domain does not match the URL |
| "Your connection is not private" after renewal | Apache not reloaded, or old cert still referenced |
| Mixed-content warnings | Page loads images/scripts over http:// |
| SSL_ERROR_NO_CYPHER_OVERLAP | Only old protocols enabled; add TLS 1.2/1.3 |
Mixed content is the one that surprises people after a migration: the certificate is perfect, but a single <img src="http://..."> makes the browser drop the padlock. Serve every asset over HTTPS, and add upgrade-insecure-requests to your Content-Security-Policy to catch stragglers.
chmod 600, never commit or email it.Advertisement
Written by
Amit VermaFounder / Senior Software Engineer
Amit leads engineering at Being Idea. With 15+ years building scalable software products across global markets, he drives architecture decisions and engineering culture across every engagement.
More articles by AmitWe ship software that scales. Let's work together.