Notes on how to configure HTTPS/SSL with Nginx (on a Linode Ubuntu server)

Without any introduction or discussion, here are the notes I made while learning how to get HTTPS working with Nginx. These are just for me, but if something helps you, cool.


In the end this wasn’t hard, I just needed to find a couple of good resources. It doesn’t take long at all once you know what you’re doing.

New Linode Server

  • set up a new server
  • install Ubuntu 16.04
  • turn on firewall
  • create non-root user
  • shut off root ssh access
  • point a domain name at the IP address

Update Everything

sudo apt-get update && sudo apt-get upgrade
sudo apt-get upgrade nginx (or 'install')

Ubuntu Firewall


sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw enable


sudo ufw allow 80
sudo ufw allow 443


sudo ufw status verbose


sudo ufw disable
sudo ufw app list  (output below)
    Available applications:
        Nginx Full
        Nginx HTTP
        Nginx HTTPS
sudo ufw allow 'Nginx HTTPS'

Add a New User


sudo adduser username

add user to “sudo” group:

sudo usermod -aG sudo username


sudo adduser username sudo
  • note: sudo does not work, but i can su -

Disabling Root Login (sshd_config)

sudo vim /etc/ssh/sshd_config
PermitRootLogin no
sudo service sshd restart

Limit Login Attempts (sshd_config)


PermitRootLogin no
LoginGraceTime 120
# allow only 1 login attempt per connection
MaxAuthTries 1

more info:

Install Nginx and MySQL

Install NginX:

$ sudo apt-get update
$ sudo apt-get install nginx ssl-cert


The following additional packages will be installed:
  fontconfig-config fonts-dejavu-core libfontconfig1 libgd3 libjbig0 libjpeg-turbo8 libjpeg8 libtiff5
  libvpx3 libxpm4 libxslt1.1 nginx-common nginx-core
Suggested packages:
  libgd-tools fcgiwrap nginx-doc openssl-blacklist
The following NEW packages will be installed:
  fontconfig-config fonts-dejavu-core libfontconfig1 libgd3 libjbig0 libjpeg-turbo8 libjpeg8 libtiff5
  libvpx3 libxpm4 libxslt1.1 nginx nginx-common nginx-core ssl-cert

Install MySQL on Ubuntu:

$ sudo apt-get install mysql-server
$ mysql_secure_installation

Adjust Firewall

Use these commands, as needed:

sudo ufw allow 80
sudo ufw allow 443
sudo ufw allow 'Nginx HTTPS'

Nginx Configuration

Conf file:



nginx -t
service nginx reload (stop, start)

Can also use these:

systemctl status nginx
sudo systemctl stop nginx
sudo systemctl start nginx

Nginx log files:


Basic config:

server {
    listen       80;
    root         /var/www/default;
    index        index.html;


server {
    rewrite ^(.*)$1 permanent;

server {
    listen       80;
    location / {
        root /var/www/html;
    access_log  /var/log/nginx/fptracker_access.log  main;
    error_log   /var/log/nginx/fptracker_error.log  error;

Installing Java on Ubuntu

DigitalOcean has good notes on installing Java, but the basic command to install the OpenJDK JRE is:

apt-get install default-jre

and the command to install the OpenJDK JDK/SDK is:

apt-get install default-jdk

NOT what I used: Let’s Encrypt on Ubuntu 16.04

“TLS/SSL works by using a combination of a public certificate and a private key. The SSL key is kept secret on the server. It is used to encrypt content sent to clients. The SSL certificate is publicly shared with anyone requesting the content. It can be used to decrypt the content signed by the associated SSL key.”

(1) Create a cert (openssl)

  • Common Name (e.g. server FQDN or YOUR name) is most important

$ openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /etc/ssl/private/aja-nginx-selfsigned.key -out /etc/ssl/certs/aja-nginx-selfsigned.crt

Generating a 2048 bit RSA private key
writing new private key to '/etc/ssl/private/nginx-selfsigned.key'
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:Alaska
Locality Name (eg, city) []:Talkeetna
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Valley Programming
Organizational Unit Name (eg, section) []:Home Office
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []

(2) Create a strong Diffie-Hellman group

  • create a strong Diffie-Hellman group, which is used in negotiating Perfect Forward Secrecy with clients
$ sudo openssl dhparam -out /etc/ssl/certs/aja-dhparam.pem 2048

"this is going to take a long time ..."
  • when it's done you will have a strong DH group at /etc/ssl/certs/aja-dhparam.pem that we can use in our configuration
  • took less than a minute on the new test server

(3) Configure Nginx to Use SSL

create a new Nginx configuration snippet in the /etc/nginx/snippets directory:

sudo vi /etc/nginx/snippets/aja-self-signed.conf

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

Set Nginx up with a strong SSL cipher suite and enable some advanced features that will help keep our server secure. The parameters we will set can be reused in future Nginx configurations, so we will give the file a generic name:

sudo nano /etc/nginx/snippets/aja-ssl-params.conf
  • add our preferred DNS resolver for upstream requests
  • set the ssl_dhparam setting to point to the Diffie-Hellman file we generated earlier
    // sudo nano /etc/nginx/snippets/aja-ssl-params.confssl-params.conf
    # from
    # and

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ecdh_curve secp384r1;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver valid=300s;
    resolver_timeout 5s;
    # Disable preloading HSTS for now.  You can use the commented out header line that includes
    # the "preload" directive if you understand the implications.
    #add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
    add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;

    ssl_dhparam /etc/ssl/certs/aja-dhparam.pem;

Adjust the Nginx Configuration to Use SSL

(NOT NEEDED FOR ME) back up our current server block file:

sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak
sudo vi /etc/nginx/sites-available/default

(Alternative Configuration) Allow Both HTTP and HTTPS Traffic

  • allow both encrypted and unencrypted content
  • generally not recommended if it can be avoided, but in some situations it may be necessary
  • just compress the two separate server blocks into one block and remove the redirect
  • TODO: get rid of the "default" file in sites-enabled. i think the default_server config from there is colliding with me
  • note that default is a symlink (default -> /etc/nginx/sites-available/default)
server {
    #listen 80 default_server;
    #listen [::]:80 default_server;
    #listen 443 ssl http2 default_server;
    #listen [::]:443 ssl http2 default_server;

    # these work without "default_server"
    listen 80;
    listen [::]:80;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name server_domain_or_IP;
    include snippets/aja-self-signed.conf;
    include snippets/aja-ssl-params.conf;

`nginx -t` output is normal:

nginx: [warn] "ssl_stapling" ignored, issuer certificate not found
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Adjust the Firewall

sudo ufw allow 'Nginx Full'
sudo ufw status

Enable the Changes in Nginx

$ sudo nginx -t
$ service nginx restart

Test in Browser

kinda-sorta expected errors:

- "your connection is not secure"
- The certificate is not trusted because it is self-signed. 
~ (GONE THE SECOND TIME) The certificate is not valid for the name

Nginx "default_server"

  • The default_server parameter has been available since version 0.8.21.
  • In this configuration nginx tests only the request’s header field “Host” to determine which server the request should be routed to. If its value does not match any server name, or the request does not contain this header field at all, then nginx will route the request to the default server for this port. In the configuration above, the default server is the first one — which is nginx’s standard default behaviour. It can also be set explicitly which server should be default, with the default_server parameter in the listen directive:

    server { listen 80 defaultserver; servername; ... }

Can change to a permanent redirect (301)

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name server_domain_or_IP;
    return 301 https://$server_name$request_uri;

More Security: Preventing Information Disclosure

  • /etc/nginx/nginx.conf:
http {
    server_tokens off;  <-- ADD THIS (show only 'nginx', no version number)

More Security: Fail2Ban

Restricting Access by IP Address

server {
    location /wp-admin/ {
        deny  all;

See also