Guides
Installing an SSL Certificate on nginx, Apache and IIS
Installation is three files and a reload. What goes wrong is almost always the chain: a certificate that works in your browser and fails on someone else's device.
Run a fresh DomainLens audit and use the report as your priority list.
The files you will have
Order matters in a combined file: the leaf certificate first, then each intermediate in the direction of the root. Reversing it produces a chain some clients accept and others reject, which is the hardest version of this problem to diagnose.
| File | Contains | Note |
|---|---|---|
| Private key | Your secret key | Never leaves the server; permissions 600 |
| Certificate | Your public certificate | Sometimes called the leaf |
| Intermediate(s) | The chain to a trusted root | Must be served with the certificate |
| Full chain | Leaf plus intermediates in one file | What nginx expects |
| Root | The trust anchor | Already in clients — do not serve it |
nginx
The single most common nginx mistake is pointing ssl_certificate at the leaf certificate instead of the full chain. It works in browsers that already cached the intermediate and fails everywhere else.
This is deceptive precisely because it half works. Your own browser has almost certainly collected the intermediate from some other site, so it completes the chain without complaint and the installation looks finished. A mobile client on a fresh install, an API consumer, or a monitoring agent has no such cache and fails outright — which is why the fault usually surfaces as a report from someone else rather than in your own testing.
The private key permissions deserve a moment too. The file should be readable only by root, with the web server reading it before dropping privileges. A key that is world-readable is a real exposure, and it is easy to create accidentally when copying files between servers.
server {
listen 443 ssl;
http2 on;
server_name example.com www.example.com;
# fullchain, not the leaf alone — nginx does not
# fetch intermediates for you
ssl_certificate /etc/ssl/example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
}
# Test the config before reloading
# sudo nginx -t && sudo systemctl reload nginx
Apache and IIS
- IIS works from a PKCS#12 bundle: convert PEM to .pfx with openssl, then import through the certificates console.
- In IIS, bind the certificate to the site in Site Bindings — importing alone does not put it in use.
- IIS needs the SNI checkbox when several hostnames share one address.
- Windows builds the chain from its own store, so a missing intermediate can work on Windows and fail on Linux clients.
<VirtualHost *:443>
ServerName example.com
SSLEngine on
SSLCertificateFile /etc/ssl/example.com/cert.pem
SSLCertificateKeyFile /etc/ssl/example.com/privkey.pem
SSLCertificateChainFile /etc/ssl/example.com/chain.pem
SSLProtocol -all +TLSv1.2 +TLSv1.3
</VirtualHost>
# apachectl configtest && systemctl reload apache2
#
# Apache 2.4.8+ also accepts a full chain in
# SSLCertificateFile, making ChainFile unnecessary.
Verifying from outside
- 1Test the configuration syntax before reloading — both nginx and Apache have a check command.
- 2Reload rather than restart where possible, so existing connections are not dropped.
- 3Confirm the served certificate matches the file you installed, using openssl s_client from another machine.
- 4Check the chain is complete with -showcerts; a single certificate in the output means intermediates are missing.
- 5Test from a device that has never visited the site, since your browser may have cached an intermediate.
- 6Confirm every hostname on the certificate resolves and serves it — a SAN entry pointing elsewhere still fails.
# Dates and issuer of what is actually served
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
# Full chain — expect leaf plus intermediates
openssl s_client -connect example.com:443 -showcerts 2>/dev/null \
| grep -c 'BEGIN CERTIFICATE'
# A result of 1 means the chain is incomplete.
How DomainLens contributes
After installing, the question is what the internet sees rather than what the config says. The SSL checker reads the live handshake from outside your network, so a cached intermediate in your own browser cannot mask an incomplete chain. For automation see certbot and Let's Encrypt , and for the commands themselves see OpenSSL commands .
- Why does my certificate work in Chrome but fail elsewhere?
- Almost certainly an incomplete chain. Your browser cached the intermediate from another site; a client that has not will fail to build a path to a trusted root.
- What is the difference between cert.pem and fullchain.pem?
- cert.pem is your certificate alone. fullchain.pem is that plus the intermediates. nginx wants fullchain, because it does not fetch intermediates itself.
- Do I need to restart the server after installing?
- A reload is enough and does not drop connections. Certificates are read at start-up, so some form of reload is mandatory — the file alone changes nothing.
- Can one certificate serve several sites on one server?
- Yes, if every hostname appears on it as a SAN entry or is covered by a wildcard. Otherwise each site needs its own certificate with SNI.