BLOG POSTS
    MangoHost Blog / How to Create a Self-Signed SSL Certificate for Nginx in Ubuntu
How to Create a Self-Signed SSL Certificate for Nginx in Ubuntu

How to Create a Self-Signed SSL Certificate for Nginx in Ubuntu

Self-signed SSL certificates provide a quick and cost-effective way to enable HTTPS on your Nginx server, particularly useful for development environments, internal applications, or proof-of-concept deployments. While they won’t provide the trust validation of commercial certificates, they encrypt traffic just as effectively and help you test SSL configurations before going live. This guide walks you through creating and implementing self-signed certificates on Ubuntu, covering everything from generation to troubleshooting common issues.

Understanding Self-Signed SSL Certificates

Self-signed certificates are digital certificates that you create and sign yourself, rather than having them validated by a trusted Certificate Authority (CA). The encryption strength remains identical to commercial certificates, but browsers will display security warnings since they can’t verify the certificate’s authenticity through a trusted chain.

Here’s how they compare to other certificate types:

Certificate Type Cost Browser Trust Setup Time Best Use Case
Self-Signed Free Warnings displayed 5-10 minutes Development, internal apps
Let’s Encrypt Free Fully trusted 10-15 minutes Public websites, production
Commercial SSL $50-500/year Fully trusted Hours to days Enterprise, e-commerce

Prerequisites and Setup Requirements

Before jumping into certificate creation, ensure your Ubuntu system meets these requirements:

  • Ubuntu 18.04 or newer (tested on 20.04 and 22.04)
  • Nginx installed and running
  • OpenSSL package (usually pre-installed)
  • Root or sudo access
  • Basic understanding of Nginx configuration

Verify OpenSSL installation:

openssl version -a

If you need Nginx, install it quickly:

sudo apt update
sudo apt install nginx
sudo systemctl start nginx
sudo systemctl enable nginx

Step-by-Step Certificate Creation Process

The certificate creation involves generating a private key and then creating the certificate itself. We’ll use a single command approach for efficiency, but I’ll also show the step-by-step method for better understanding.

Method 1: Single Command Generation

Create both the private key and certificate in one shot:

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

You’ll be prompted for certificate details. Here’s what each field means and suggested values:

Country Name (2 letter code) [AU]: US
State or Province Name (full name) [Some-State]: California
Locality Name (eg, city) []: San Francisco
Organization Name (eg, company) [Internet Widgits Pty Ltd]: Your Company
Organizational Unit Name (eg, section) []: IT Department
Common Name (e.g. server FQDN or YOUR name) []: your-domain.com
Email Address []: admin@your-domain.com

The Common Name field is crucial – it should match your domain name or server IP address.

Method 2: Step-by-Step Generation

For better control over the process, generate the key and certificate separately:

# Generate private key
sudo openssl genrsa -out /etc/ssl/private/nginx-selfsigned.key 2048

# Generate certificate signing request
sudo openssl req -new -key /etc/ssl/private/nginx-selfsigned.key \
    -out /tmp/nginx-selfsigned.csr

# Generate self-signed certificate
sudo openssl x509 -req -days 365 -in /tmp/nginx-selfsigned.csr \
    -signkey /etc/ssl/private/nginx-selfsigned.key \
    -out /etc/ssl/certs/nginx-selfsigned.crt

# Clean up CSR file
sudo rm /tmp/nginx-selfsigned.csr

Advanced: Custom Configuration File

For repeated certificate generation or specific requirements, create a configuration file:

sudo nano /tmp/cert.conf
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req

[dn]
C = US
ST = California
L = San Francisco
O = Your Organization
OU = IT Department
CN = your-domain.com

[v3_req]
subjectAltName = @alt_names

[alt_names]
DNS.1 = your-domain.com
DNS.2 = www.your-domain.com
DNS.3 = localhost
IP.1 = 192.168.1.100

Generate certificate using the config:

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

Configuring Nginx for SSL

Now that we have our certificates, let’s configure Nginx to use them. Create a new server block or modify an existing one:

sudo nano /etc/nginx/sites-available/your-site-ssl

Basic SSL configuration:

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    
    server_name your-domain.com www.your-domain.com;
    
    ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
    ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
    
    # Basic SSL settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
    
    # Document root and other settings
    root /var/www/html;
    index index.html index.htm index.nginx-debian.html;
    
    location / {
        try_files $uri $uri/ =404;
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    listen [::]:80;
    
    server_name your-domain.com www.your-domain.com;
    
    return 301 https://$server_name$request_uri;
}

For production-ready SSL configuration with enhanced security:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    
    server_name your-domain.com;
    
    # SSL Configuration
    ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
    ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
    
    # Enhanced SSL Security
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    
    # HSTS (optional for self-signed certs)
    add_header Strict-Transport-Security "max-age=63072000" always;
    
    # SSL Session Settings
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 10m;
    
    # OCSP stapling (disabled for self-signed)
    # ssl_stapling on;
    # ssl_stapling_verify on;
    
    root /var/www/html;
    index index.html index.htm;
    
    location / {
        try_files $uri $uri/ =404;
    }
}

Enable the site and restart Nginx:

sudo ln -s /etc/nginx/sites-available/your-site-ssl /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Real-World Use Cases and Examples

Self-signed certificates shine in several scenarios where commercial certificates might be overkill or impractical:

Development Environment Setup

Perfect for local development where you need to test SSL-dependent features:

# Create development certificate for localhost
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -keyout /etc/ssl/private/localhost.key \
    -out /etc/ssl/certs/localhost.crt \
    -subj "/C=US/ST=Dev/L=Local/O=Development/CN=localhost"

Internal Network Applications

For intranet applications where users can accept certificate warnings:

# Certificate for internal IP
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -keyout /etc/ssl/private/internal.key \
    -out /etc/ssl/certs/internal.crt \
    -subj "/C=US/ST=Internal/L=Network/O=Company/CN=192.168.1.100"

API Testing and Development

When building APIs that require SSL for webhook testing or third-party integrations:

server {
    listen 443 ssl;
    server_name api.internal.com;
    
    ssl_certificate /etc/ssl/certs/api-selfsigned.crt;
    ssl_certificate_key /etc/ssl/private/api-selfsigned.key;
    
    location /api/v1/ {
        proxy_pass http://localhost: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;
    }
}

Performance Considerations and Optimization

Self-signed certificates have the same performance characteristics as commercial certificates, but here are some optimization tips:

Configuration Impact Recommended Setting
SSL Session Cache Reduces CPU usage by 50-80% shared:SSL:10m
Key Size 2048 vs 4096 bit performance 2048 (good security/performance balance)
HTTP/2 Support 20-30% faster page loads Always enable with SSL
SSL Protocols Security vs compatibility TLSv1.2 and TLSv1.3 only

Optimized SSL configuration for performance:

# Add to your server block for better performance
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
ssl_buffer_size 8k;

# Enable HTTP/2
listen 443 ssl http2;
listen [::]:443 ssl http2;

Troubleshooting Common Issues

Here are the most frequent problems you’ll encounter and their solutions:

Certificate and Key Mismatch

Verify your certificate and key match:

# Check certificate
openssl x509 -noout -modulus -in /etc/ssl/certs/nginx-selfsigned.crt | openssl md5

# Check key
openssl rsa -noout -modulus -in /etc/ssl/private/nginx-selfsigned.key | openssl md5

The MD5 hashes should be identical.

Permission Issues

Ensure proper permissions on certificate files:

sudo chmod 644 /etc/ssl/certs/nginx-selfsigned.crt
sudo chmod 600 /etc/ssl/private/nginx-selfsigned.key
sudo chown root:root /etc/ssl/certs/nginx-selfsigned.crt
sudo chown root:root /etc/ssl/private/nginx-selfsigned.key

Nginx Configuration Errors

Common configuration mistakes and fixes:

# Test configuration
sudo nginx -t

# Common error: missing semicolon
# Fix: add semicolon at end of each directive

# Common error: wrong file paths
# Fix: verify files exist and paths are correct
ls -la /etc/ssl/certs/nginx-selfsigned.crt
ls -la /etc/ssl/private/nginx-selfsigned.key

Browser Security Warnings

For development purposes, you can bypass browser warnings:

  • Chrome: Type “thisisunsafe” when seeing the warning
  • Firefox: Click “Advanced” β†’ “Accept the Risk and Continue”
  • For automated testing: Use curl with -k flag: curl -k https://your-domain.com

Security Best Practices and Limitations

While self-signed certificates provide encryption, follow these security practices:

Certificate Management

  • Set reasonable expiration dates (365 days maximum)
  • Store private keys securely with 600 permissions
  • Use strong key sizes (minimum 2048 bits)
  • Regularly rotate certificates
  • Keep certificate details consistent

Network Security

# Create stronger Diffie-Hellman parameters
sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048

# Add to Nginx configuration
ssl_dhparam /etc/nginx/dhparam.pem;

Monitoring and Maintenance

Create a script to check certificate expiration:

#!/bin/bash
# check-ssl-expiry.sh

CERT_FILE="/etc/ssl/certs/nginx-selfsigned.crt"
EXPIRY_DATE=$(openssl x509 -enddate -noout -in "$CERT_FILE" | cut -d= -f2)
EXPIRY_SECONDS=$(date -d "$EXPIRY_DATE" +%s)
CURRENT_SECONDS=$(date +%s)
DAYS_UNTIL_EXPIRY=$(( (EXPIRY_SECONDS - CURRENT_SECONDS) / 86400 ))

echo "Certificate expires in $DAYS_UNTIL_EXPIRY days"

if [ $DAYS_UNTIL_EXPIRY -lt 30 ]; then
    echo "WARNING: Certificate expires soon!"
fi

Migration Path to Production Certificates

When you’re ready to move from self-signed to production certificates, here’s your migration strategy:

Let’s Encrypt Migration

Install Certbot and obtain free certificates:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com -d www.your-domain.com

Commercial Certificate Migration

Replace certificate files while keeping the same Nginx configuration structure:

# Replace these lines in your Nginx config:
ssl_certificate /path/to/commercial/certificate.crt;
ssl_certificate_key /path/to/commercial/private.key;

# Test and reload
sudo nginx -t
sudo systemctl reload nginx

Whether you’re setting up a development environment on a VPS or preparing internal applications on dedicated servers, self-signed SSL certificates provide a solid foundation for encrypted communications. They’re particularly valuable for learning SSL configuration, testing HTTPS-dependent applications, and securing internal network traffic where certificate authority validation isn’t required.

For additional technical documentation and SSL best practices, refer to the official Nginx SSL documentation and OpenSSL documentation.



This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.

This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.

Leave a reply

Your email address will not be published. Required fields are marked