Nginx [pronounced "engine x"] is an open-source web server software designed with high concurrency in mind, that can be used as HTTP/HTTPS server, reverse proxy server, mail proxy server, software load balancer, TLS terminator, caching server, generic TCP/UDP proxy server, etc.

Nginx is one of the most widely used web servers available today. It's used by many well-known companies around the globe such as Yandex, VK, Mail.ru, Dropbox, Netflix, WordPress.com, FastMail, etc.

Nginx is extremely modular software. Even some of the seemingly "built-in" pieces of the software, such as GZIP or SSL, are actually developed as modules that can be enabled and disabled during the build time.

Nginx has core (native) modules and third-party (external) modules created by the community. Right now, there are over a hundred third-party modules that we can utilize.

Written in C language, it's a speedy and lightweight piece of software.
Installing Nginx from source code is relatively "easy" - download the latest version of Nginx source code, configure, build, and install it.
You’ll need to choose whether to download the mainline or a stable version, but building them is the same.

In this guide, we will compile the latest mainline version of Nginx (which is at 1.17.10 at the time of this writing) on Debian 10 (buster) system. We will use all available modules in the open-source version of Nginx. Update version numbers when newer versions become available.

NOTE: Try to manage software with the package manager as much as you can. Stay away from compiling software manually to the extent possible.

Why compile and install Nginx from source

You probably ask why would one compile Nginx from a source when you can use prepared packages. Here are some reasons why you may want to compile specific software yourself:

  • To control configuration options.
  • To install the software anywhere you like. You can even install several different versions of the same software.
  • To control the version that you install. Distributions don’t always stay up-to-date with the latest versions of all packages, particularly add-ons to software packages.
  • To better understand how the software works.

Stable vs. mainline version

Nginx Open Source is available in two versions:

  • Mainline – Includes the latest features and bug fixes and is always up to date. It is reliable, but it may include some experimental modules, and it may also have some new bugs.
  • Stable – Doesn’t include all of the latest features, but has critical bug fixes that are always backported to the mainline version.

Core modules vs. third-party modules

Nginx has two types of modules that you can utilize: core modules and third-party modules.

The core Nginx developers build core modules, and they are part of the software itself.

The community builds third-party modules, and you can use them to extend Nginx functionality. There are a lot of helpful third-party modules. The most famous among them are - PageSpeed, ModSecurity, RTMP, Lua, etc...

Static modules vs. dynamic modules

Static modules exist in Nginx from the very first version. Dynamic modules were introduced with Nginx 1.9.11+ in February 2016. With static modules, a set of modules that constitute an Nginx binary is fixed at compile time by the ./configure script.

Static modules use --with-foo_bar_module or --add-module=PATH syntax.

To compile core (standard) module as dynamic we add =dynamic, for example --with-http_image_filter_module=dynamic.

To compile the third-party module as dynamic, we use --add-dynamic-module=/path/to/module syntax, and then we load them by using load_module directive in the global context of the nginx.conf file.

Requirements for building Nginx from source

In comparison with some other UNIX/Linux software, Nginx is pretty lightweight and doesn’t have many library dependencies. The default build configuration depends on only 3 libraries to be installed: OpenSSL/LibreSSL/BoringSSL, Zlib and PCRE.

NOTE: Nginx can also be compiled against LibreSSL and BoringSSL crypto libraries instead of OpenSSL.

Before you begin

Check the Debian version.

lsb_release -ds
# Debian GNU/Linux 10 (buster)

Create a regular user with sudo access.

adduser johndoe --gecos "John Doe"
usermod -aG sudo johndoe

NOTE: Replace johndoe with your username.

Switch to a new user.

su - johndoe

Set up the timezone.

sudo dpkg-reconfigure tzdata

Update your operating system’s software.

sudo apt update && sudo apt upgrade -y

Install the necessary packages.

sudo apt install -y software-properties-common ufw

Build Nginx from source

Nginx is a program written in C, so you will first need to install a compiler tool. Install build-essential, git and tree packages.

sudo apt install -y build-essential git tree

Download the latest mainline version of the Nginx source code and unpack the source code archive. Nginx source code is distributed as a compressed archive (gzipped tarball .tar.gz), like most Unix and Linux software.

wget https://nginx.org/download/nginx-1.17.10.tar.gz && tar zxvf nginx-1.17.10.tar.gz

Download the mandatory Nginx dependencies' source code and extract it.

# PCRE version 8.44
wget https://ftp.pcre.org/pub/pcre/pcre-8.44.tar.gz && tar xzvf pcre-8.44.tar.gz

# zlib version 1.2.11
wget https://www.zlib.net/zlib-1.2.11.tar.gz && tar xzvf zlib-1.2.11.tar.gz

# OpenSSL version 1.1.1g
wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz && tar xzvf openssl-1.1.1g.tar.gz

Install optional Nginx dependencies.

sudo apt install -y perl libperl-dev libgd3 libgd-dev libgeoip1 libgeoip-dev geoip-bin libxml2 libxml2-dev libxslt1.1 libxslt1-dev

Clean up all .tar.gz files. We don't need them anymore.

rm -rf *.tar.gz

Enter the Nginx source directory.

cd ~/nginx-1.17.10

For good measure list directories and files that compose Nginx source code with tree utility.

tree -L 2 .

Copy Nginx manual page to /usr/share/man/man8/ directory.

sudo cp ~/nginx-1.17.10/man/nginx.8 /usr/share/man/man8
sudo gzip /usr/share/man/man8/nginx.8
ls /usr/share/man/man8/ | grep nginx.8.gz
# Check that man page for Nginx is working
man nginx

For help, you can see a full list of up-to-date Nginx compile-time options by running.

./configure --help
# To see want core modules can be built as dynamic run:
./configure --help | grep -F =dynamic

Configure, compile and install Nginx.

./configure --prefix=/etc/nginx \
            --sbin-path=/usr/sbin/nginx \
            --modules-path=/usr/lib/nginx/modules \
            --conf-path=/etc/nginx/nginx.conf \
            --error-log-path=/var/log/nginx/error.log \
            --pid-path=/var/run/nginx.pid \
            --lock-path=/var/run/nginx.lock \
            --user=nginx \
            --group=nginx \
            --build=Debian \
            --builddir=nginx-1.17.10 \
            --with-select_module \
            --with-poll_module \
            --with-threads \
            --with-file-aio \
            --with-http_ssl_module \
            --with-http_v2_module \
            --with-http_realip_module \
            --with-http_addition_module \
            --with-http_xslt_module=dynamic \
            --with-http_image_filter_module=dynamic \
            --with-http_geoip_module=dynamic \
            --with-http_sub_module \
            --with-http_dav_module \
            --with-http_flv_module \
            --with-http_mp4_module \
            --with-http_gunzip_module \
            --with-http_gzip_static_module \
            --with-http_auth_request_module \
            --with-http_random_index_module \
            --with-http_secure_link_module \
            --with-http_degradation_module \
            --with-http_slice_module \
            --with-http_stub_status_module \
            --with-http_perl_module=dynamic \
            --with-perl_modules_path=/usr/share/perl/5.26.1 \
            --with-perl=/usr/bin/perl \
            --http-log-path=/var/log/nginx/access.log \
            --http-client-body-temp-path=/var/cache/nginx/client_temp \
            --http-proxy-temp-path=/var/cache/nginx/proxy_temp \
            --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
            --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
            --http-scgi-temp-path=/var/cache/nginx/scgi_temp \
            --with-mail=dynamic \
            --with-mail_ssl_module \
            --with-stream=dynamic \
            --with-stream_ssl_module \
            --with-stream_realip_module \
            --with-stream_geoip_module=dynamic \
            --with-stream_ssl_preread_module \
            --with-compat \
            --with-pcre=../pcre-8.44 \
            --with-pcre-jit \
            --with-zlib=../zlib-1.2.11 \
            --with-openssl=../openssl-1.1.1g \
            --with-openssl-opt=no-nextprotoneg \
            --with-debug

make
sudo make install

Following the compilation, navigate to your home (~) directory.

cd ~

Symlink /usr/lib/nginx/modules to /etc/nginx/modules directory. etc/nginx/modules is a standard place for Nginx modules.

sudo ln -s /usr/lib/nginx/modules /etc/nginx/modules

Print the Nginx version, compiler version, and configure script parameters.

sudo nginx -V

# nginx version: nginx/1.17.10 (Debian)
# built by gcc 8.3.0 (Debian 8.3.0-6)
# built with OpenSSL 1.1.1g  28 Feb 2020
# TLS SNI support enabled
# configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules . . .
# . . .
# . . .

Create an Nginx system group and user.

sudo adduser --system --home /nonexistent --shell /bin/false --no-create-home --disabled-login --disabled-password --gecos "nginx user" --group nginx
# Check that user and group are created
sudo tail -n 1 /etc/passwd /etc/group /etc/shadow

Check Nginx syntax and potential errors.

sudo nginx -t
# Will throw this error -> nginx: [emerg] mkdir() "/var/cache/nginx/client_temp" failed (2: No such file or directory)

# Create NGINX cache directories and set proper permissions
sudo mkdir -p /var/cache/nginx/client_temp /var/cache/nginx/fastcgi_temp /var/cache/nginx/proxy_temp /var/cache/nginx/scgi_temp /var/cache/nginx/uwsgi_temp
sudo chmod 700 /var/cache/nginx/*
sudo chown nginx:root /var/cache/nginx/*

# Re-check syntax and potential errors. 
sudo nginx -t

Create an Nginx systemd unit file.

sudo vim /etc/systemd/system/nginx.service

Copy/paste the below content into /etc/systemd/system/nginx.service file.

[Unit]
Description=nginx - high performance web server
Documentation=https://nginx.org/en/docs/
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -c /etc/nginx/nginx.conf
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID

[Install]
WantedBy=multi-user.target

Enable Nginx to start on boot and start Nginx immediately.

sudo systemctl enable nginx.service
sudo systemctl start nginx.service

Check if Nginx will automatically initiate after a reboot.

sudo systemctl is-enabled nginx.service
# enabled

Check the Nginx status.

sudo systemctl status nginx.service
NOTE: You can verify that Nginx is running by going to your site’s domain or IP address in a web browser. You should see the Nginx welcome page. That's an indicator that Nginx is up and running on your VPS.

Create an Uncomplicated Firewall (UFW) Nginx application profile.

sudo vim /etc/ufw/applications.d/nginx

Copy/paste the below content into /etc/ufw/applications.d/nginx file.

[Nginx HTTP]
title=Web Server (Nginx, HTTP)
description=Small, but very powerful and efficient web server
ports=80/tcp

[Nginx HTTPS]
title=Web Server (Nginx, HTTPS)
description=Small, but very powerful and efficient web server
ports=443/tcp

[Nginx Full]
title=Web Server (Nginx, HTTP + HTTPS)
description=Small, but very powerful and efficient web server
ports=80,443/tcp

Validate that UFW application profiles are created and recognized.

sudo ufw app list

# Available applications:
  # Nginx Full
  # Nginx HTTP
  # Nginx HTTPS
  # OpenSSH

Nginx by default, generates backup .default files in /etc/nginx. Remove .default files from /etc/nginx directory.

sudo rm /etc/nginx/*.default

Place syntax highlighting of Nginx configuration for Vim editor into ~/.vim.

# For regular non-root user
mkdir ~/.vim/
cp -r ~/nginx-1.17.10/contrib/vim/* ~/.vim/
# For root user
sudo mkdir /root/.vim/
sudo cp -r ~/nginx-1.17.10/contrib/vim/* /root/.vim/

NOTE: By doing the above step, you will get a nice syntax highlighting when editing Nginx configuration files in Vim editor.

Create conf.d, snippets, sites-available and sites-enabled directories in /etc/nginx directory.

sudo mkdir /etc/nginx/{conf.d,snippets,sites-available,sites-enabled}

Change permissions and group ownership of Nginx log files.

sudo chmod 640 /var/log/nginx/*
sudo chown nginx:adm /var/log/nginx/access.log /var/log/nginx/error.log

Create a log rotation config for Nginx.

sudo vim /etc/logrotate.d/nginx

Populate the file with the below text, then save and exit.

/var/log/nginx/*.log {
    daily
    missingok
    rotate 52
    compress
    delaycompress
    notifempty
    create 640 nginx adm
    sharedscripts
    postrotate
            if [ -f /var/run/nginx.pid ]; then
                    kill -USR1 `cat /var/run/nginx.pid`
            fi
    endscript
}

Remove all downloaded files from the home (~) directory.

cd ~
rm -rf nginx-1.17.10/ openssl-1.1.1g/ pcre-8.44/ zlib-1.2.11/

Summary

That's it. Now, you have the latest version of Nginx installed from source code. It is compiled statically against some important libraries like OpenSSL. Often, the system-supplied version of OpenSSL is outdated. By using this method of installing with a newer version of OpenSSL, you can take advantage of modern ciphers such as CHACHA20_POLY1305 and protocols such as TLS 1.3 that is supported in OpenSSL 1.1.1. Moreover, by compiling your binary, you can tailor what functionality your Nginx will provide, which is much more flexible than installing a pre-built binary. Also, compiling from source is a very laborious job, but nothing prevents you to wrap all these steps in a ad hoc shell script and automate this process.