Yong Sen - Full-Stack Developer

Generate a Self‑Signed SSL Certificate on Ubuntu (Nginx)

Step-by-step guide to creating a self-signed certificate with OpenSSL and configuring Nginx with secure defaults.

January 20, 2025
2 min read

Generate a Self‑Signed SSL Certificate on Ubuntu (Nginx)

This guide shows you how to generate a self‑signed TLS certificate with OpenSSL and configure Nginx to serve HTTPS using secure defaults. Self‑signed certificates are great for internal environments, staging, and quick local testing. For public sites, use Let's Encrypt.

1) Generate Self‑Signed Certificate and Key

sudo openssl req -x509 -nodes -days 365 \
  -newkey rsa:2048 \
  -keyout /etc/ssl/private/nginx-selfsigned.key \
  -out /etc/ssl/certs/nginx-selfsigned.crt

Explanation of flags:

  • openssl req -x509: Create an X.509 certificate (self‑signed when using -x509).
  • -nodes: Do not encrypt the private key (no passphrase, required for unattended Nginx reloads).
  • -days 365: Certificate validity period.
  • -newkey rsa:2048: Generate a new 2048‑bit RSA key.
  • -keyout: Output path for the private key.
  • -out: Output path for the certificate.

During prompts, set the Common Name (CN) to your domain or server IP (e.g., example.com or 203.0.113.10).

(Optional) Generate Strong DH Parameters

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

2) Configure Nginx

Edit your server block (e.g., /etc/nginx/sites-available/default):

server {
    listen 80;
    listen 443 ssl;
    server_name your_domain_or_ip;

    ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
    ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;

    # Recommended SSL settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
    # Uncomment if you created dhparam.pem
    # ssl_dhparam /etc/ssl/certs/dhparam.pem;

    # HSTS (enable only if sure, includes subdomains)
    # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # Example content (static)
    location / {
        root /var/www/html;
        index index.html;
    }

    # Or reverse proxy example
    # location /api/ {
    #     proxy_pass http://127.0.0.1:3000/;
    #     proxy_set_header Host $host;
    #     proxy_set_header X-Real-IP $remote_addr;
    #     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    #     proxy_set_header X-Forwarded-Proto $scheme;
    # }
}

Replace your_domain_or_ip with your domain (preferred) or IP.

3) Test and Reload Nginx

sudo nginx -t
sudo systemctl reload nginx

4) Trusting the Certificate (Local/Dev)

Browsers will show a warning for self‑signed certs. For internal use, you can trust the cert locally:

  • Export /etc/ssl/certs/nginx-selfsigned.crt and import into your OS/browser trust store.
  • For Chrome/Edge on Windows: import via "Manage computer certificates" → Trusted Root Certification Authorities.
  • For macOS: Keychain Access → System → Certificates → import and set to Always Trust.

Troubleshooting

  • Port 443 blocked: sudo ufw allow 443 (and 80 if redirecting HTTP).
  • Permissions: key file should be readable by root only (chmod 600 /etc/ssl/private/nginx-selfsigned.key).
  • Wrong CN: regenerate with correct Common Name or use SubjectAltName (SAN) with an OpenSSL config.

Production Note

Use Let's Encrypt (Certbot) for public websites to get a free and trusted certificate:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com

This config gets you running securely for testing and internal services. For internet‑facing sites, switch to a trusted CA as soon as possible.

Post Details

January 20, 2025
2 min read
Tags
UbuntuNginxSSLTLSOpenSSLDevOps