DEV Community

Dima Stopel
Dima Stopel

Posted on • Originally published at cert-depot.com

Self-Signed SSL Certificate for Nginx — Step-by-Step Guide

Originally published on cert-depot.com. Free, open-source self-signed certificate generator — no signup, keys never stored.

Self-Signed SSL Certificate for Nginx

A practical step-by-step guide. Generates a working HTTPS setup in under five minutes.

Self-signed certificates are perfect for local development, staging servers, internal tools, and learning how TLS works. Browsers will display a warning because the certificate isn't signed by a trusted CA, but the traffic itself is encrypted the same way as with a Let's Encrypt cert.

Step 1: Generate the Certificate

The fastest way is to use cert-depot.com — enter your domain, click generate, and download the ZIP. You'll get two files: certificate.pem and private-key.pem.

If you prefer the command line with OpenSSL:

openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout private-key.pem -out certificate.pem \
  -days 365 \
  -subj "/CN=localhost" \
  -addext "subjectAltName=DNS:localhost,DNS:example.local,IP:127.0.0.1"
Enter fullscreen mode Exit fullscreen mode

Important: Always include a Subject Alternative Name (SAN). Modern browsers (Chrome 58+, released 2017) ignore the Common Name field and will reject certificates without a matching SAN, even if the CN is correct.

Step 2: Install the Files

Place the certificate and key somewhere Nginx can read them. A common convention is /etc/nginx/ssl/:

sudo mkdir -p /etc/nginx/ssl
sudo cp certificate.pem /etc/nginx/ssl/
sudo cp private-key.pem /etc/nginx/ssl/
sudo chmod 600 /etc/nginx/ssl/private-key.pem
sudo chown root:root /etc/nginx/ssl/*
Enter fullscreen mode Exit fullscreen mode

The chmod 600 on the private key is critical — it should not be readable by any user other than root.

Step 3: Configure the Server Block

Edit your site configuration (typically /etc/nginx/sites-available/default or a file under /etc/nginx/conf.d/):

server {
    listen 443 ssl;
    server_name example.local;

    ssl_certificate     /etc/nginx/ssl/certificate.pem;
    ssl_certificate_key /etc/nginx/ssl/private-key.pem;

    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    location / {
        root /var/www/html;
        index index.html;
    }
}

# Optional: redirect HTTP to HTTPS
server {
    listen 80;
    server_name example.local;
    return 301 https://$server_name$request_uri;
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Test and Reload

Always test the configuration before reloading Nginx to avoid downtime:

sudo nginx -t
sudo systemctl reload nginx
Enter fullscreen mode Exit fullscreen mode

If nginx -t reports errors, fix them before reloading. Common issues: typo in file paths, missing file, or wrong file permissions.

Step 5: Verify HTTPS is Working

Use curl to confirm the certificate is being served (the -k flag tells curl to ignore the untrusted-certificate warning):

curl -kvI https://example.local/
Enter fullscreen mode Exit fullscreen mode

You should see the certificate details in the output. To inspect exactly what's being served:

openssl s_client -connect example.local:443 -servername example.local \
  </dev/null 2>/dev/null | openssl x509 -text -noout
Enter fullscreen mode Exit fullscreen mode

Or paste the certificate into our PEM decoder to see its details in a friendlier format.

Troubleshooting

Browser shows "Your connection is not private"

This is expected with a self-signed certificate. In Chrome, click "Advanced" and then "Proceed to example.local (unsafe)". To avoid the warning entirely, you need to trust the certificate in your system's certificate store.

Nginx won't start with "cannot load certificate"

Check that the certificate file exists and Nginx can read it. Run sudo ls -la /etc/nginx/ssl/ and verify the paths in your config match exactly.

Certificate errors despite the correct CN

This almost always means your certificate doesn't have a matching Subject Alternative Name. Regenerate it including the hostname as a SAN — see Step 1.

Further Reading

Top comments (0)