[1/2] Install NGINX with Let's Encrypt on Debian 9 (automatic certificate renewal)

In this serie of guides, I’m going to explain how to install NGINX with Let’s Encrypt and an automatic renewal of SSL certificates. All of this on a Debian 9 server.

By following this guide steps, you’re going to have a NGINX webserver ready to serve static files, PHP websites and also .NET Core web applications!

Prerequisites

  • A domain name (OVH sells domains here)
  • A wildcard entry in your domain name configuration that points on your root domain site
  • A VPS or dedicated server with admin rights and SSH access (OVH also sells VPS and servers!)
  • An example website (static html files)
  • (if necessary) A PHP Website and PHP installed on your server (PHP 7.2 for this guide but you can adapt the instructions)
  • (if necessary) A ASP․NET Core website and ASP․NET Core installed on your server

Preparation

In the first part of this guide, we’re going to only install NGINX and configure Let’s Encrypt but you must already think of the file structure of your website.

I won’t say I have the best practices or the best security but this is how I, a hobbyist sysadmin, have set my own environment. I’ll use one of my latest Linux environments as an real-life example for this guide but won’t use the real domain and subdomains names.

Available websites

Let’s say our domain name is contoso.com and we have several subsites:

1
2
3
- contoso.com           (ASP.Net Core Web Application)
- blog.contoso.com (Static HTML blog)
- php.contoso.com (PHP Website)

On the server filesystem, these website will be located here:

1
2
3
- contoso.com      => /home/www/contoso.com
- blog.contoso.com => /home/www/blog.contoso.com
- php.contoso.com => /home/www/php.contoso.com

NGINX will have a configuration file for each sites (3) and a default configuration file. We’ll also need to add all subdomains to our SSL certificate (blog and php).

Don’t forget to add you sites files now in the right subfolders

NGINX Installation

As always, before installing a new package, we update our repositories and update, if necessary, our installed packages:

1
sudo apt-get update && sudo apt-get upgrade

Then we can install NGINX. The package will add a new service to systemd and will start it immediatly.

1
sudo apt-get install nginx

Check if you successfully have installed NGINX by getting its systemd status:

1
sudo service nginx status

Expected output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[16:56]lyyn@mayako:~$ sudo service nginx status
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2018-08-24 16:56:17 CEST; 29s ago
Docs: man:nginx(8)
Process: 11745 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Process: 11742 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Main PID: 11746 (nginx)
Tasks: 2 (limit: 4915)
CGroup: /system.slice/nginx.service
├─11746 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
└─11747 nginx: worker process

Aug 24 16:56:17 vps562376 systemd[1]: Starting A high performance web server and a reverse proxy server...
Aug 24 16:56:17 vps562376 systemd[1]: nginx.service: Failed to read PID from file /run/nginx.pid: Invalid argument
Aug 24 16:56:17 vps562376 systemd[1]: Started A high performance web server and a reverse proxy server.

If you can read “Active: active (running)”, your NGINX has been successfully started.

Configuration

NGINX configuration is located in /etc/nginx/. You set up your different servers (= domains / subdomains) in the sites-available folder and you activate them by creating a symlink in the sites-enabled folder.

The default config file will just show the default NGINX page, you can leave it as it is for now but this is a good time to check if you can see this page: in your browser, simply type http://<your server ip> and you should have the following page shown:

If you get an error, check the following:

  • you have an internet access (yes I know but a double check won’t kill anyone)
  • NGINX is still running on your server (type sudo service nginx status)
  • You don’t have a firewall on your webserver that blocks the port 80 (TCP)

Setting up our sites

I assume that you already have added your sites files in the right server folders

So, we need to create 3 files, one for each sites:

1
2
3
sudo touch /etc/nginx/sites-available/contoso.com
sudo touch /etc/nginx/sites-available/blog.contoso.com
sudo touch /etc/nginx/sites-available/php.contoso.com

Let’s configure all of them…

1. contoso․com (ASP․Net Core Web Application)

Edit this site configuration file:

1
sudo nano /etc/nginx/sites-available/contoso.com

Assuming that your ASP.Net Core Web Application is running and listening on the port 5000, you’re going to have this configuration file:

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 80;
server_name contoso.com;

location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $http_host;
proxy_cache_bypass $http_upgrade;
}
}

Here, NGINX act as a reverse proxy: it gets the HTTP requests and redirect them, locally, to the Kestrel webserver. When a response is ready, Kestrel give it to NGINX and NGINX will returns with this data.

Configuration file explanation

Each server block describe a new site, or subsite. You can set a specific behavior per site by using this server block.

Properties used:

  • listen: Listening port to use, 80 is the default http port

  • server_name: DNS name to listen to. If someone enters http://contoso.com, NGINX will know that it will have to apply the rules in this specific file

  • location /: Here, this is used to give a default behavior from the root site level, that will be inherited by all URL paths

    • proxy_pass: Set this to the URL listened by the targeted application of the reverse proxy. Our ASP.Net Core Web app listen to the port 5000, so I set that to http://localhost:5000
    • proxy_http_version: Proxy version to use. 1.1 allow to use “keepalive” connections and “NTLM authentication” (only used on Windows OS)
    • proxy_set_header: Some HTTP headers to add to each request, from the reverse proxy to the web application (values suggested by Microsoft)
    • proxy_cache_bypass: Conditions under which the response won’t be taken from the cache (value suggested by Microsoft, again)

2. blog․contoso․com (Static HTML blog)

A static HTML website is simply a website without any server calculation.

Edit this site configuration file:

1
sudo nano /etc/nginx/sites-available/blog.contoso.com

This site configuration file will have this content:

1
2
3
4
5
6
7
server {
listen 80;
server_name blog.contoso.com;

root /home/lyyn/www/blog;
index index.html index.htm;
}

(New) Properties used:

  • root: Indicates where are this site files
  • index: Tell NGINX what are the default files to read (here, if index.html doesn’t exist, NGINX will try to read index.htm before sending an error)

This is the simplest configuration file as there is no computation done by a specific web server.

3. php․contoso․com (PHP Website)

Here, NGINX will use FastCGI technology to act as a bridge for PHP, and this last one will be use to dynamically generate the pages.

Edit this site configuration file:

1
sudo nano /etc/nginx/sites-available/php.contoso.com

And this site configuration will have this content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server {
listen 80;
server_name php.contoso.com;

root /home/www/php.contoso.com;
index index.php index.html index.htm;

# pass the PHP scripts to FastCGI server listening on /run/php/php7.2-fpm.sock
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
fastcgi_index index.php;
fastcgi_intercept_errors on;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

I assume that you have PHP7.2 installed, but you can adapt the fastcgi_pass socket path to your proper PHP-FPM executable

(New) Properties used

  • try_files: Here, each request will target a specific file or folder, if these files or folders don’t exist, it will throw a 404 error
  • fastcgi_pass: Address of the FastCGI server. Here, this is the PHP-FPM socket
  • fastcgi_index: If the URL ends with a “/”, the $fastcgi_script_name variable will be set to this variable value
  • fastcgi_intercept_errors: Tells NGINX to intercept the HTTP error codes thrown by the FastCGI server (PHP-FPM)
  • fastcgi_param: For the param “SCRIPT_FILENAME”, set the value “documentrootdocument_rootfastcgi_script_name”. Here, document root will be “/home/www/php.contoso.com” and $fastcgi_script_name will be the script name to execute (here, by default, it will be “index.php”)
  • include: Used here to include the fastcgi_param parameters (only one here, but you can set even more if you want)

NGINX configuration registration

Currently, we only have prepared our files but they won’t be applied by NGINX. We need to create a symlink to our files, from /etc/nginx/sites-enabled/ to /etc/nginx/sites-available. Let’s do this:

1
2
3
sudo ln -s /etc/nginx/sites-available/contoso.com /etc/nginx/sites-enabled/contoso.com
sudo ln -s /etc/nginx/sites-available/blog.contoso.com /etc/nginx/sites-enabled/blog.contoso.com
sudo ln -s /etc/nginx/sites-available/php.contoso.com /etc/nginx/sites-enabled/php.contoso.com

Before reloading NGINX and thus applying our new sites configuration, we should if we don’t have any configuration error:

1
nginx -t

If no error are reported, you can safely restart NGINX:

1
sudo service nginx restart

Now we’re going to install Let’s Encrypt but this will be explained in this blog post: [2/2] Install NGINX with Let's Encrypt on Debian 9 (automatic certificate renewal)