Install an SSL certificate on nginx
Nginx needs three things to serve HTTPS: the certificate file with its chain, the private key, and a server block that listens on port 443 with the ssl parameter. The configuration is short, and most broken installations come from a chain that was not combined or a listen line without ssl. This page covers the configuration, the redirect from HTTP, TLS versions, and nginx in front of an application.
The steps common to every server are on installing a certificate.
Before you start
The certificate and its intermediates, as separate files or already combined. Many authorities and ACME clients provide a combined file, often named
fullchain.pem. A PFX bundle, the usual import format when you install an SSL certificate on IIS, has to be converted to PEM files first. The OpenSSL commands for that split are part of exporting an SSL certificate to another server.The private key generated with the request, covered on CSR.
Access to edit the configuration and reload nginx.
The configuration file that serves this site. On a server with several sites there are several, and editing the wrong one changes nothing.
If you obtain certificates with an ACME client, it can usually write the files and reload nginx itself. See ACME clients.
Combine the certificate and chain
Nginx reads the site's certificate and its intermediates from one file, in that order: the site's certificate first, then the intermediate that signed it. The nginx documentation describes the same order. Current Apache versions read the same combined file, as covered under how to install an SSL certificate on Apache.
$ cat example.com.crt intermediate.crt > example.com.chained.crtReversed order makes nginx present the intermediate as the site's certificate, and nginx refuses to start because it no longer matches the key. Leaving the intermediate out produces no error at all: browsers that already know the intermediate connect, and other clients fail with unable to get local issuer certificate. The root does not belong in the file; clients already have it. See certificate chains.
Place the files
Keep both files outside the web root and restrict the key:
$ chmod 600 example.com.key
$ chown root:root example.com.keyWarning. The nginx master process reads the key as root at startup, before the worker processes drop privileges, so the key does not need to be readable by the worker user. Making it readable by everyone to clear a permissions error turns a configuration problem into an exposed key. Check the path and ownership instead.
Configure the server block
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name example.com www.example.com;
ssl_certificate /etc/ssl/example.com.chained.crt;
ssl_certificate_key /etc/ssl/example.com.key;
# the rest of the site configuration
}listen 443 sslturns on TLS for the port. Without thesslparameter nginx answers on port 443 in plain HTTP, and browsers report ERR_SSL_PROTOCOL_ERROR or, in Firefox,SSL_ERROR_RX_RECORD_TOO_LONG. See port 443.http2 onenables HTTP/2. It is a separate directive since nginx 1.25.1; older guides puthttp2on thelistenline, a form that newer versions accept with a deprecation warning. On versions before 1.25.1, keeplisten 443 ssl http2;and leave out the directive.server_namemust list every hostname this block serves, and the certificate must cover each of them. A name served here but missing from the certificate produces a name mismatch in the browser, not an nginx error. See Subject Alternative Names.
Set TLS versions
ssl_protocols TLSv1.2 TLSv1.3;That is the default in current nginx, so on a recent version the line only states it explicitly. Older versions enabled TLS 1.0 and 1.1 by default, and configurations copied from old guides often list them too. Where they appear, remove them. Cipher lists change more often than versions; rather than copying one from a guide, generate a current one with the Mozilla SSL Configuration Generator. Why the versions matter is on TLS versions, and what a bad cipher list produces is on cipher mismatch.
Redirect HTTP to HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}The redirect only works after the HTTPS connection succeeds, so it cannot help a name the certificate does not cover. Once HTTPS works for every name, HSTS tells browsers to skip the HTTP step on later visits.
Test and reload
$ nginx -t
$ nginx -s reloadnginx -t checks the configuration and names the file and line of any error. Reload only after it reports success: nginx keeps running the old configuration if the new one fails, but testing first shows you why. A reload applies the change without dropping open connections, so a full restart is not needed. On systems managed by systemd, systemctl reload nginx does the same.
If nginx reports key values mismatch, the certificate and the key do not belong together, usually because a reissued certificate was installed next to the old key. The certificate and key matcher confirms it in one step.
Verify from outside
Check from a machine other than the server, against each hostname:
$ curl -I https://example.com/
$ openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/nullThe first confirms that the site answers over HTTPS without a certificate error. The second shows which certificate is served, whether the intermediate is sent, and the verification result. Your own browser is the weakest test here, because it may already hold the intermediate that the server fails to send. More checks are on OpenSSL commands.
Several sites on one server
Nginx chooses the server block by the hostname the client sends during the handshake, called SNI. If no block matches, the request goes to the default server for that port: the block marked default_server, or the first one defined. That block's certificate is presented.
So a hostname with no block of its own receives another site's certificate, and the visitor sees a name mismatch that looks like a certificate problem and is a configuration one. Adding a site means adding both its server_name and its certificate. One block can serve several names that share a certificate; names with separate certificates need separate blocks.
Nginx in front of an application
A common setup puts nginx in front of an application server, such as Node.js, Python or Java. Nginx handles TLS with the configuration above and forwards plain HTTP to the application on the same machine:
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}The application then sees requests as HTTP. X-Forwarded-Proto tells it the visitor used HTTPS, which matters for redirects and secure cookies; most frameworks need a setting to trust that header.
When the application is on another machine and the connection must be encrypted too, use proxy_pass https://. By default nginx does not check the upstream certificate and does not send the upstream hostname. Turn both on:
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
proxy_ssl_server_name on;The defaults are documented in the proxy module. Without verification, the connection to the application is encrypted but not authenticated.
If nginx itself sits behind a load balancer or a CDN that terminates TLS, visitors see the certificate on that layer, not this one.
Common mistakes on nginx
The chain was not combined.
ssl_certificatepoints at the site's certificate alone. Browsers often cope; scripts, apps and other clients fail.The listen line has no ssl parameter. Port 443 answers in plain HTTP.
Two blocks claim the same name. Nginx uses one of them and warns about a conflicting server name; the certificate in the other is never presented.
The configuration was tested and not reloaded.
nginx -tchecks; it does not apply.
Automate this with Bridge SSL
Bridge SSL issues certificates and serves them with their full chain, so the combination step, the file placement and the reload disappear from the process. Renewal happens on a schedule rather than as work on the server, which removes the case where a certificate is renewed correctly and nginx never picks it up. See what the platform offers at Bridge CDN.
FAQ
How do I install an SSL certificate on nginx?
Combine the certificate and its intermediates into one file, point ssl_certificate at it and ssl_certificate_key at the private key in a server block with listen 443 ssl, then run nginx -t and reload.
Does nginx need the chain in the same file as the certificate?
Yes. The site's certificate comes first, followed by the intermediates. Without them, some browsers still connect and most other clients fail with an issuer error.
Why does my nginx site answer with plain HTTP on port 443?
The listen directive is missing the ssl parameter, so nothing encrypts the port. Browsers report ERR_SSL_PROTOCOL_ERROR or SSL_ERROR_RX_RECORD_TOO_LONG.
Do I need to restart nginx after installing a certificate?
No. Test with nginx -t, then reload with nginx -s reload or systemctl reload nginx. A reload applies the new certificate without dropping open connections.
How do I enable HTTP/2 with SSL on nginx?
On nginx 1.25.1 and later, add http2 on; to the server block. On older versions, add http2 to the listen directive: listen 443 ssl http2;.
Why is nginx serving the wrong certificate?
No server block matches the requested hostname, so the default server for the port answers with its certificate. Add the hostname to server_name in the right block, or create a block for it.