If you only need encryption for internal server connections or non-user sites, signing your own SSL certificates is an easy way to avoid dealing with an external CA. Here is how to configure it in nginx.

If you are more interested in getting free SSL certificates, you can always use Allows to encrypt, which is best suited for public servers with user-oriented websites, as it will show up as from a recognized certification authority in users’ browsers. However, it cannot be used to encrypt private IP addresses, so you must sign a certificate yourself.

Automatically generate and sign an SSL certificate

To do this, we will use the openssl utility. You probably have it installed already, as it is a dependency of Nginx. But if it is missing somehow, you can install it from your distro’s package manager. For Debian based systems like Ubuntu this would be:

sudo apt-get install openssl

After openssl is installed, you can generate the certificate with the following command:

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

You will be asked for information about your organization. Because it is self-signed, the only one that really matters is the “Common Name”, which should be set to your domain name or your server’s IP address.

Country Name (2 letter code) []:
State or Province Name (full name) []:
Locality Name (eg, city) []:
Organization Name (eg, company) []:
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []: your_ip_address
Email Address []:

It will take a second to generate a new RSA private key, used to sign the certificate, and store it in /etc/ssl/private/nginx.key. The certificate itself is stored in /etc/ssl/certs/nginx.crt, and is valid for a whole year.

We also want to generate a Diffie-Hellman group. This is used for perfect transmission confidentiality, which generates ephemeral session keys to ensure that past communications cannot be decrypted if the session key is compromised. It’s not quite necessary for internal communications, but if you want to be as secure as possible, you shouldn’t skip this step.

sudo openssl dhparam -out /etc/nginx/dhparam.pem 4096

It takes a while, about an hour depending on your server speed. Have lunch and come back to your terminal for a bit to configure Nginx.

RELATED: What is a PEM file and how do you use it?

Configure Nginx to use your private key and SSL certificate

To make things easier, we’ll put all the configuration in a snippet file that we can include in our nginx server blocks. Create a new config snippet in nginx snippets phone book:

touch /etc/nginx/snippets/self-signed.conf

Open it in your favorite text editor and paste the following into:

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

ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver valid=300s;
resolver_timeout 5s;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

ssl_dhparam /etc/nginx/dhparam.pem;
ssl_ecdh_curve secp384r1;

The first two lines of this snippet configure nginx to use our own certificate and private key. The next block is general SSL settings, and finally the last two lines configure nginx to use our Diffie-Hellman group for downstream security. You can omit it if you don’t feel like waiting.

The only other thing to enable would be HTTP Strict Transport Security, which configures your site to always use SSL. This would require a permanent redirect from HTTP to HTTPS, so you should verify that SSL is working before enabling it.

Now change your main nginx config (usually located at /etc/nginx/nginx.conf for unique sites, or under your domain name in /etc/nginx/sites-available for multi-site servers) and source the extract:

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    include snippets/self-signed.conf;

    server_name example.com www.example.com;
    . . .

You’ll also want to configure redirect from HTTP to HTTPS, which you can do with an additional server block listening on port 80:

server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    return 302 https://$server_name$request_uri;

This is a 302 redirect, which is only temporary. You’ll want to upgrade to 301 if everything is working fine.

Test your setup by restarting nginx:

sudo service nginx restart

Since HTTPS traffic uses port 443, you will need to configure your firewalls to allow transport on this port. If you use iptables or UFW, you will need to open the ports from the command line. If you’re using a hosting service like AWS that has a built-in firewall, you’ll also need to open it from its web interface.

AWS Firewall Interface

If your service operates entirely within your local network, you may want to whitelist your specific subnet of IP addresses to disable access from outside the local network and access your servers through a network. VPN connection.

If everything is working correctly, you should now be able to access your server over HTTPS. Your web browser may display a warning like this:

chrome SSL warning

Don’t worry, this is normal and is the reason why you cannot use these certificates for customer-facing websites. You will need to manually confirm that you trust the server to access it.

The warning displayed here is slightly misleading: your site is secure as long as the private key is not compromised, and it is perfectly secure if you configure Diffie-Hellman transmission privacy. The problem is with identity, because Chrome can’t verify that your server is who it claims to be because you signed the certificate yourself.

Once you’ve verified that there are no issues with SSL, you can switch the HTTP redirect to a 301 redirect:

return 301 https://$server_name$request_uri;

And restart nginx to apply the changes.


Please enter your comment!
Please enter your name here