Skip to main content

Nginx HTTPS from scratch

A simple step-by-step guide to setup Nginx with HTTPS from scratch. This was originally performed on a Raspberry Pi but should work fine on any Debian-based Linux (like Ubuntu). Keep in mind that I'm still a noob at this, so this guide might not result in the best configuration but at least it works!

Run Updates

If you're working on a fresh Linux install (especially on a Raspberry Pi), run these commands to update it:

sudo apt update
sudo apt upgrade
sudo apt dist-upgrade
sudo apt full-upgrade
sudo apt clean
sudo apt autoremove
sudo reboot

Initialize Nginx

  1. Install Nginx: sudo apt install nginx
  2. Start Nginx: sudo systemctl start nginx

Setup a firewall

This firewall will be setup to only allow local connections (connections within your LAN) for SSH, HTTP, and HTTPS. If you want connections outside of your LAN, you'll need to modify the IP mask part of each rule (192.168.0.0/16).

  1. Install: sudo apt install ufw
  2. SSH: sudo ufw allow from 192.168.0.0/16 to any port 22
  3. HTTP: sudo ufw allow from 192.168.0.0/16 to any port 80
  4. HTTPS: sudo ufw allow from 192.168.0.0/16 to any port 443
  5. Nginx HTTP: sudo ufw allow from 192.168.0.0/16 to any app 'Nginx HTTP'
  6. Nginx HTTPS: sudo ufw allow from 192.168.0.0/16 to any app 'Nginx HTTPS'
  7. OpenSSH: sudo ufw allow from 192.168.0.0/16 to any app OpenSSH

Verify your firewall rules with sudo ufw show added.

Enable the firewall with sudo ufw enable. You can check its status with sudo ufw status.

If you mess up a rule, you can list all added rules with their ids using sudo ufw status numbered and then delete a rule with sudo ufw delete <number>.

Test Nginx

At this point, you should be able to hit the default Nginx server from any device on your local network. Simply navigate to the IP address of your Nginx device in a browser to see it.

If this doesn't work, something is blocking connections to Nginx or Nginx itself isn't working.

Create a self-signed SSL certificate

If you don't care about serving anyone outside of your LAN, you can simply create a self-signed SSL certificate for HTTPS.

  1. Create the certificate: sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt;
  2. Fill out the prompts.
  3. Create DH Parameters (for Nginx): sudo openssl dhparam -out /etc/nginx/dhparam.pem 4096;

Setup Nginx with HTTPS

  1. Create a self-signed SSL certificate Nginx snippet:

    1. Create and edit the snippet: sudo nano /etc/nginx/snippets/self-signed.conf
    2. Paste in the following:
      ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
      ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
  2. Create an SSL params snippet:

    1. Create and edit the snippet: sudo nano /etc/nginx/snippets/ssl-params.conf

    2. Paste in the following:

      ssl_protocols TLSv1.3;
      ssl_prefer_server_ciphers on;
      ssl_dhparam /etc/nginx/dhparam.pem;
      ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
      ssl_ecdh_curve secp384r1;
      ssl_session_timeout 10m;
      ssl_session_cache shared:SSL:10m;
      ssl_session_tickets off;
      ssl_stapling on;
      ssl_stapling_verify on;
      add_header X-Frame-Options DENY;
      add_header X-Content-Type-Options nosniff;
      add_header X-XSS-Protection "1; mode=block";
  3. Create a site with HTTPS:

    1. Create and edit a new site file (replace my-site with the name of your site): sudo nano /etc/nginx/sites-available/my-site

    2. Paste in the following (modify http://localhost:3000 to use whatever port your server will be running on locally):

      server {
      listen 443 ssl;
      listen [::]:443 ssl;
      include snippets/self-signed.conf;
      include snippets/ssl-params.conf;

      location / {
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_pass http://localhost:3000;
      }
      }

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

      return 301 https://$host$request_uri;
      }
  4. If you haven't already, disable the default site configuration: sudo rm /etc/nginx/sites-enabled/default

  5. Enable your newly created site: sudo ln -s /etc/nginx/sites-available/my-site /etc/nginx/sites-enabled/

  6. Verify your Nginx configuration: sudo nginx -t

    1. If you see a warning about ss_stapling, you can ignore it. This is caused by your ssl certificate being self signed.
  7. Restart Nginx (you'll need to to this any time you change your Nginx configuration): sudo systemctl restart nginx

  8. Start your website on the port configured in my-site. (3000 in the example above.)

Visiting your HTTPS website

You should now be able to hit your self-signed, self-hosted, LAN-only HTTPS website by simply visiting the IP of your site host in a browser on any device in your LAN. You'll have to force your browser to visit it or trust the SSL certificate (because your site's SSL certificate is self-signed which is typically a no-no but doesn't really matter on LAN).