Securely Set up a Website with Nginx

nginx Jan 29, 2020

NGINX is a very powerful web server that is now also used as a reverse proxy, HTTP cache, and load balancer. NGINX is built to offer low memory usage and high concurrency. Rather than creating new processes for each web request, NGINX uses an asynchronous, event-driven approach where requests are handled in a single thread.

This guide assumes that you are trying to host a basic static website on a remote server. For the purposes of this guide, we will use the domain name qwerty.com and the IP address 123.123.123.123 as examples with Debian 10 Buster as Operating System. and that files for your website are located at /var/www/qwerty.com Replace these with your actual domain name and IP address wherever required.

Prerequisites

  • Basic Knowledge of Linux
  • Remotely hosted server
  • All the static files of your website
  • No Prior knowledge of NGINX

Step 1 : Setup a Remote Server

I recommend to set up a remote server using Scaleway. But it is up to you, it doesn't matter.


Step 2 : Setup DNS

Your domain name needs to point to your server IP address. Create an A record in your hosting provider’s DNS settings, pointing your domain name qwerty.com to the server IP address 123.123.123.123.

You can follow this link. Every vendor will have a different console but process will be same.


Step 3 : Install NGINX

Login to your server instance using SSH. Then run the following commands.

sudo apt-get update && apt-get upgrade
sudo apt-get install nginx -y

Step 4 : Configure NGINX to host your Website

You need to tell NGINX about your website and how to serve it.
First, the basics..

  • NGINX directory where all configuration files are saved - /etc/nginx
  • we are mainly interested in two directories.
    • /etc/nginx/sites-available - Contains original configuration files, which will be editing later.
    • /etc/nginx/sites-enabled - Contains the files NGINX actually reads.

What we have to basically do is to create a configuration file in sites-available directory and then create a symbolic link to point to that file in sites-enabled directory to actually tell NGNIX to run it.

Create a file named qwerty.com in sites-available directory and add the following text to it:

server {
  listen 80 default_server;
  listen [::]:80 default_server;
  root /var/www/qwerty.com;
  index index.html;
  server_name qwerty.com;
  location / {
    try_files $uri $uri/ =404;
  }
}

Let's understand what we just did.

  • This is server block and any request for qwerty.com will be served by this server block.
  • It is listening on port 80.
  • Root folder is where we kept the files for our website
  • Index tells us the main index file of your website

Save the file and close it. Now let's add this file to sites-enabled folder to tell NGINX to read it. Execute the following command:
ln -s /etc/nginx/sites-available/qwerty.com /etc/nginx/sites-enabled/qwerty.com

The syntax for symbolic link command is as follows:
ln -s <source_file> <destination_file>

Let's test if all the changes that we have made are correct. Run the following command.
sudo nginx -t
The output should look something like this.

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

If there are errors, there might be an issue with the syntax and you should correct it.

Now just restart the NGINX and you should be able to see your site.
sudo systemctl restart

Now you can visit qwerty.com on any browser of your choice and should be able to see your website.


Step 5 : Enable HTTPS

All of your websites should always be protected by HTTPS, even if they do not handle sensitive communications. Apart from providing basic protection and data integrity for both your websites and personal information about your users, HTTPS is a necessity for many new browser features, particularly those needed for progressive web applications.

First, we are going to procure a new SSL certificate for our website and then we will direct NGINX to use that certificate for our website.

There are a few things we need to consider before getting SSL certificates. You can obtain a single domain certificate, but if subdomains exist, you may want to obtain a wild-card certificate that can be extended to all subdomains. You can also obtain a single domain certificate for a specific subdomain. This is the choice you make.

We are going to use Certbot for this purpose.

sudo apt-get install certbot python-certbot-nginx
sudo certbot certonly --nginx

If you are using a different operating system, you can get specific commands here. Just select NGINX and OS that you are using.

Follow the instructions on screen and complete the process. The certificate will be saved at this location.

/etc/letsencrypt/live/qwerty.com/

The certificate we installed will need renewal regularly. To automatically renew the certificate use:

sudo crontab -e
17 7 * * * certbot renew --post-hook "systemctl reload nginx"

Now that we have acquired an SSL certificate let's configure our website to use it. For that, we will need to modify the configuration file we used for qwerty.com.
Inside the server block, add the following text:

server {
   # All that we have already added in previous step
   
   ssl on;
   ssl_certificate /etc/letsencrypt/live/qwerty.com/fullchain.pem;
   ssl_certificate_key /etc/letsencrypt/live/qwerty.com/privkey.pem;

In addition to that, our server block is listening to port 80 as of now, which is default port for HTTP communication. However, SSL connection uses port 443 so we need to change the port as well.

server {
   listen 443 default_server;
   listen [::]:443 default_server;
   # Everything else
}

We also need to redirect all port 80 traffic to port 443 to ensure that someone trying to connect via HTTP be able to visit the website. To enable this, add the following server block after the current server block:

server {
       listen 0.0.0.0:80;
       server_name qwerty.com;
       rewrite ^ https://$host$request_uri? permanent;
}

Now we just need to restart NGINX and your website will be using SSL connection.

sudo systemctl restart nginx

Now that we have setup a secure SSL connection for our website, we still need to secure our SSL connection itself.

Disable Insecure SSL-TLS Protocols + Cipher Suites + Diffie-Hellman key

First, let's generate a strong DH key. Run the following command.

openssl dhparam -out /etc/ssl/dhparams.pem 2048

This will take some time. You can replace 2048 with 4096. Using a 4096 connection will further improve the security but performance will decrease. 2048 is already very far into the "cannot break it zone"

Note: If generating a 2048-bit DH modulus takes 1 hour on average, the same machine with the same software will use an average of 16 hours for a 4096-bit DH modulus.

Now, open /etc/nginx/nginx.conf in a text editor of your choice and locate SSL settings in the file. Change the configurations to look like this:

ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA HIGH !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
ssl_dhparam    /etc/ssl/dhparams.pem;

Add these as SSL Ciphers

ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA HIGH !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";

Save the file and reload NGINX.

nginx -s reload

Check the SSL Certificate status on Qualys SSL Labs. Grade should be at-least A+

Step 6: Adding Security Headers, hiding server token and Custom Error Page

Adding Security Headers

HTTP response headers can be used to increase the security of your application. Once set, these HTTP response headers can restrict modern browsers from running into easily preventable vulnerabilities. The OWASP Secure Headers Project can explain all the headers in detail.

Add the following lines to your server block.

add_header "Referrer-Policy" "strict-origin";
add_header Feature-Policy "geolocation none;";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload;" always;
add_header "X-XSS-Protection" "1; mode=block";
add_header "X-Content-Type-Options" "nosniff" always;
add_header "X-Frame-Options" "DENY" always;
add_header "X-Permitted-Cross-Domain-Policies" "master-only";
add_header Content-Security-Policy "default-src 'self';";

The configuration values can be changed as per your own requirements.
You can check if the headers have been implemented correctly here.

Configuring a Custom Error Page

Custom error page needs to be created in website directory as well. here we will use 404.html as custom error page HTML file.

Add the following content to your configuration file

error_page 404 /404.html;
   location = /404.html {
        root /var/www/qwerty.com;
        internal;
  }

   if ($host != "qwerty.com") {
         return 404;
  }

This will ensure that the the website is redirected to a custom error page.

Removing Server tokens

Edit the /etc/nginx/nginx.conf file and add the following content.

server_tokens off;

Now we need to restart the NGINX server.

sudo systemctl restart nginx

Step 7: Improving the performance

Now that we have securely set up a website, let's try to improve the performance of the website. We will take following steps to improve the performance.

1. Enabling HTTP/2

HTTP/2 helps to improve the website loading speed significantly. Just add http2 to the server block as follows:

server {
   listen 443 http2 default_server;
   listen [::]:443 http2 default_server;
   # Everything else
}
2. Enabling gzip compression

gzip compression reduces the size of files during transmission. Add the following to your server block:

server {
   #All that we have already added in previous step
   gzip on;
   gzip_types application/javascript image/* text/css;
   gunzip on;
}

This will ensure that javascript files, images, and CSS files are always compressed.

3. Enabling client-side caching

There will be files that your website uses and can be cached on the client side, this helps to speed up the loading of your website.You can set cache control headers to provide hints to browsers to let them know what files they should not request again.

server {
   #...after the location / block
   location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
       expires 7d;
    }
}

To enable all these, we need to restart the NGINX server.

sudo systemctl restart nginx

That's all, your website is now up and running securely.

Anmol Nayyar

Cyber Security Analyst at KPMG India skilled in Network Infrastructure Assessment and Information Security, with a Bachelor of Technology, focused in ECE from Panjab University.