Nginx Tutorial

Advertisement

Advertisement

Overview

Intro

Nginx, pronounced "engine X", is a fast and lightweight web server, that can be used to serve static files, but is often used as a reverse proxy. It has some very nice features like load balancing and rate limiting. We'll cover some common use cases like serving files, creating a directory listing, reverse proxying to pass incoming traffic to a local web server, adding SSL encryption, and how to require https and www on your site. This guide is for someone who needs a quick reference to setting up a simple nginx server. For the latest documentation always check out the official website https://www.nginx.com/ and the source mirror at https://github.com/nginx/nginx.

Why use nginx?

Use nginx if you need to reverse proxy, load balance, and rate limit network services. Reverse proxying useful if you have multiple web services listening on various ports and you need a single public endpoint to reroute requests internally. This would allow you to host multiple domain names on port 80 while using a combination of different Node.js, Go, and Java to power separate web services behind the scenes.

By setting up nginx to listen on port 80 and 443, you can set up your other web services on low-privilege ports that listen locally only. Nginx can handle the logging, load balancing, blacklisting, and serving static files while the web services focus on what they need to do.

Personally I find the configuration of nginx easier than Apache httpd. Nginx was designed for high concurrency and is very fast. While you can configure Apache to act as a reverse proxy, I find nginx much easier to work with.

Install nginx

This covers building nginx from source, installing on Ubuntu with apt-get, downloading the pre-compiled binaries for Windows, and using brew to install it on Mac. For the latest links and information always check out the official website at https://www.nginx.com/

Build nginx from source

If possible, this is a great option to get the latest version. Linux distribution package managers often lag behind and don't have the latest version. Arch Linux is known for having relatively up-to-date packages and Debian is known for having slow release cycles where versions get stale. This will also cover how to create a basic working environment for nginx, a minimal config file, and how to start and stop the server. This was tested on Ubuntu.

The first step is to download and extract the nginx source zip or tar.gz. Find a release from the read-only GitHub repo (https://github.com/nginx/nginx/releases) or the upstream Nginx.org Mercurial repo. Find the latest release tag, like http://hg.nginx.org/nginx/rev/release-1.15.5. On the left-hand side there is a zip and gz option to download the tag, for example, http://hg.nginx.org/nginx/archive/release-1.15.5.tar.gz.

# Download & extract
wget https://github.com/nginx/nginx/archive/release-1.15.5.tar.gz
tar xzfv release-1.15.5.tar.gz
cd nginx-release-1.15.5

# Build
sudo apt-get install build-essential # If you need gcc and make
auto/configure
make
  • Executable in objs/
  • Executable file: objs/nginx
  • Sample configs in conf/
  • Default config: conf/nginx.conf

To run nginx after building it from source, you'll want to create a directory for nginx to use as it's working directory. This is where temporary directories will be created, log files will be stored relative to this location, config files will be relative to here.

# Create a workspace (prefix) for nginx
mkdir /path/to/nginx_workspace
mkdir /path/to/nginx_workspace/logs
vim /path/to/nginx_workspace/my.conf

You can test a config using the -T flag

# Test config file
nginx -T config.conf

To start and stop nginx use -s flag with a "stop" or "start" command. This sends a signal to the pid of the running nginx instance. The nginx.pid file which stores the process id of the listener is stored in the logs/ directory within the prefix/workspace directory by default. You can override the file path of the pid file.

# Start nginx
# -p is the workspace directory
# -c is the config file
./nginx -p /home/dano/nginx_workspace -c my.conf

# Stop nginx with `-s stop` flag
# Must specify the prefix/workspace dir so it knows
# which nginx instance to start/stop
./nginx -p /home/dano/nginx_workspace -c my.conf -s stop

Here is a minimalist config that you could use. It won't do anything, and it will result in a 404 if you visit http://localhost:9999, but it will run and it will listen.

events {
    worker_connections  1024;


http {
    server {
        listen 0.0.0.0:9999;
    }
}

Install nginx on Ubuntu

The easiest way to get nginx on Ubuntu is to install using apt-get. Follow the instructions below to use the official Ubuntu package. You can also build from source following the build nginx from source instructions. The build from source instructions were tested in Ubuntu and worked very smoothly. It's very satisfying when a C project builds easily from source without a lot of dependencies.

# See what version is available in the repos
apt-cache show nginx

# Install with apt-get package manager
sudo apt-get install nginx

# It will be started by default. Control it with:
sudo systemctl stop nginx
sudo systemctl start nginx
sudo systemctl restart nginx

# View logs with:
journalctl -u nginx

# Get the status with:
sudo systemctl status nginx

Important locations:

  • Base config file: /etc/nginx/nginx.conf
  • Default config: /etc/nginx/sites-available/default
  • Logs will be in /var/log/nginx/
  • Custom configs will go in /etc/nginx/conf.d/ or /etc/nginx/sites-available and then symlinked to /etc/nginx/sites-enabled
  • Default webroot: /var/www/html

Install nginx on Fedora

The easiest way to get nginx on Fedora is to install using dnf. Follow the instructions below to use the official Fedora package. You can also build from source following the build nginx from source instructions.

# Install with dnf package manager
sudo dnf install nginx

# Control it with:
sudo systemctl stop nginx
sudo systemctl start nginx
sudo systemctl restart nginx

# View logs with:
journalctl -u nginx

# Get the status with:
sudo systemctl status nginx

Important locations:

  • Base config file: /etc/nginx/nginx.conf
  • Directory for custom configs: /etc/nginx/conf.d/
  • Logs directory: /var/log/nginx/
  • Default webroot: /usr/share/nginx/html/

Install nginx on Windows

There are a couple options on Windows. One option is to download the precompiled binaries for Windows from https://nginx.org/en/download.html. After downloading, unzip the .zip file and nginx.exe will be right there, along with conf/ and logs/ directories. The html/ directory is the default webroot.

Another option is to download the source files as described in the build nginx from source section.

If you are using Windows but would like to work in a Linux environment, you also have the option to use Ubuntu with the Windows Subsystem for Linux and follow the Ubuntu installation instructions or the build nginx from source section. Alternatively, you could run an Ubuntu virtual machine or use Docker to get a Linux environment and follow the compilation or Ubuntu install instructions. If you are willing to pay, you can also rent a $5/month Ubuntu VPS from a provider like Digital Ocean or Linode.

Find the direct .zip file download link from https://nginx.org/en/download.html. For example, version 1.15.5 is downloadable at http://nginx.org/download/nginx-1.15.5.zip. After downloading, simply extract the zip file and nginx.exe is sitting in the root. All you have to do is run it. You could just double-click nginx.exe and then it's running and you can visit it at http://localhost:80 using curl or your web browser.

# Assuming the .zip contents were extracted to C:\nginx\
# Start nginx with this command. Config files (-c) relative to prefix dir (-p)
C:\nginx\nginx.exe -p C:\nginx\ -c conf\nginx.conf

# Stop it with
C:\nginx\nginx.exe -p C:\nginx\ -c conf\nginx.conf -s stop

You can create different prefix/workspace directories and pass that with the -p flag, but the extracted directory is already build to act as a workspace, containing the default conf\nginx.conf, logs\ directory, and html\ webroot configured.

Important locations:

  • Configs in: C:\nginx\conf\
  • Default config: C:\nginx\conf\nginx.conf
  • Logs will be in: C:\nginx\logs\
  • Default webroot is: C:\nginx\html\
  • Default listen address: http://localhost:80

Install nginx on Mac

On a Mac computer, use Homebrew or follow the build nginx from source instructions.

# Install with Homebrew
brew install nginx

# Start and stop nginx, sudo needed for low number ports
nginx 
nginx -s stop

# or control it using brew services
brew services start nginx
brew services stop nginx

Important locations:

  • Default listen address: http://localhost:8080
  • Default web root: /usr/local/var/www/
  • Default config file: /usr/local/etc/nginx/nginx.conf
  • Add custom configs in: /usr/local/etc/nginx/servers/
  • Logs will be in in: /usr/local/var/log/nginx/

Serve static files

Serving static files is the most basic task for a web server. Just drop in .txt, .html, .zip, or any other kind of file and the server will return them directly.

This configuration example will listen on port 777, can be viewed at http://localhost:7777/ and a directory listing will be provided. Also included is an example of setting separate controls on a subdirectory by turning off directory listing and designating specific index files only. It will also serve the /var/www/static-content/ directory on the url of /static/.

http {
    server {
        listen 0.0.0.0:7777;
        root /path/to/public/directory;

        location /relative-to-root/dir {
            autoindex off;
            # Default to index.htm or index.html
            index index.html, index.htm
        }

        location /static/ {
            alias /var/www/static-content/;
        }

        location / {
            # Directory listing (risky!)
            autoindex on;
        }

    }
}

Create a reverse proxy

This example configuration send traffic from port 80 to localhost:9999, including the original IP as an extra HTTP header, X-Real-IP. Some proxies use X-Forwarded-For instead.

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

        server_name mydomain.com;

        location / {
            proxy_pass http://localhost:9999/;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

If you need a simple HTTP server to test your reverse proxy configuration, you can set up quick HTTP servers with one-line commands. Check out my one-line http servers tutorial. It has examples for Python 2, Python 3, Ruby, PHP. Here's one quick example:

ruby -run -e httpd /path/to/serve -p 9999

Add SSL

This part assumes you already have a certificate and private key. If you don't, refer to my tutorial for Creating self-signed SSL certificates with OpenSSL and the LetsEncrypt Free SSL Certificate Tutorial.

Note, when working with SSL and proxying, there are two options. Which option you choose is going to depend on your needs, but here I am only covering the first option.

One option is to terminate the SSL encryption at nginx, and use plaint-text to communicate between nginx and your internal web service. This puts the burden of encrypting on nginx and puts less work on your web service. If your web service is not available publicly (except through the rproxy) and lives on the same machine as the proxy, this is generally a safe option. This is what is covered below.

The other option is to pass SSL through nginx to the final destination. This is better if your services behind the proxy live on different machines, that way the network communication over the wire is encrypted, even if it is internal. If the destination web service behind the proxy is on the same machine as the proxy, then there is less benefit to keeping it encrypted through nginx. If your application is slow at doing SSL or cannot do it at all, let nginx handle it. If your application must manage the SSL level itself, pass it through nginx.

Adding SSL to a server in nginx requires adding an ssl keyword to the listen directive and including two extra config options:

  • ssl_certificate
  • ssl_certificate_key

There are many more SSL options, which can be found at http://nginx.org/en/docs/http/ngx_http_ssl_module.html but the two minimum requirements are the certificate and the key.

# Template SSL virtual host
server {
    listen 0.0.0.0:443 ssl;
    listen [::]:443 ssl;
    server_name www.mydomain.com;

    ssl_certificate /path/to/cert.pem
    ssl_certificate_key /path/to/private-key.pem

    # Optional, set strong ciphers
    ssl_ciphers  HIGH:!aNULL:!MD5;
}

Redirect HTTP to HTTPS

If you want to redirect all HTTP traffic on port 80 to the secure HTTPS version listening on port 443, set up a permanent 301 redirect on port 80 for the domain names. This example will redirect all http traffic (whether its www or non-www prefixed) to the HTTPS version with www prefix.

# Redirect HTTP traffic to HTTPS (both www and non-www)
server {
    listen 0.0.0.0:80;
    listen [::]:80;

    server_name mydomain.com www.mydomain.com;

    # Permanent redirect to HTTPS version with www prefix
    return 301 https://www.mydomain.com$request_uri;
}

Redirect non-www to www

If you aren't using the domain name without a prefix for something, you should redirect it to www.yourdomain.com. This will reduce duplication errors and conflicts with search engines registering a page of yours twice.

# Permanent redirect of non-www prefixed traffic to www version
server {
    listen 0.0.0.0:443 ssl;
    listen [::]:443 ssl;

    server_name mydomain.com;

    ssl_certificate /path/to/cert.pem
    ssl_certificate_key /path/to/private-key.pem

    # Permanent redirect to HTTPS version with www prefix
    return 301 https://www.mydomain.com$request_uri;
}

Add HTTP Basic Authentication

To add HTTP basic auth, you simply need to configure the auth_basic and auth_basic_user_file configuration variables. To generate the user file, you will need the htpasswd executable. You can install it a couple ways:

# Fedora
sudo dnf install httpd-tools
# Ubuntu
sudo apt install apache-utils

Create a new password file with -c like this:

# Will prompt for password
htpasswd -c /path/to/.htpasswd myusername
# No password prompt
htpasswd -c /path/to/.htpasswd myusername mypassword
# Add additional users to an existing file
htpasswd /path/to/.htpasswd newuser

Then, inside the nginx config file for your server, add these lines:

server {
# ...
auth_basic "Restricted access.";
auth_basic_user_file /path/to/.htpasswd;
# ...
}

Deploy an Angular Web Application

Deploying an Angular web application is just like serving static files except to "rewrite" URLs in order for direct URLs to function, a try_files configuration is needed. By adding it, like this example demonstrates, the Angular app will behave as expected when a direct URL is visited.

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

  root /var/www/angularapp;

  location / {
    try_files $uri $uri/ /index.html;
  }

}
For a more specific and detailed tutorial on deploying Angular with nginx read Deploy Angular Apps with Nginx

Deploy Python WSGI apps like Django and Flask

I have an in-depth tutorial about deploying Django with uWSGI: How to Deploy Django with Nginx and uWSGI, but I will explain how it works with all WSGI apps here.

The Python web application will be run by a WSGI server. Waitress is easy-to-use and works on Windows, Linux, and Mac. I have a tutorial on this, Run Python WSGI Web App with Waitress. You can use this to run any WSGI app, whether it is Django, Flask, or anything else. You would typically set up this WSGI server as a system service. With Linux you can follow Creating Systemd Service Files and in Windows, Run Python Script as Windows Service.

python3 -m pip install waitress
python -m waitress --listen=127.0.0.1:8001 my_app:app

With the WSGI app listening and serving dynamic requests, you will use Nginx to reverse proxy the requests to the WSGI server. On top of this, you generally need to serve some static files which can be done with a location alias.

A typical nginx server config for a WSGI app looks something like this:

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

    server_name example.com;

    location /static/ {
        alias /path/to/app/static/;
    }

    location / {
        proxy_pass 127.0.0.1:8001;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Deploy Ruby Rack web apps with uWSGI

A specific tutorial has been dedicated to this topic and can be read at Deploy Ruby Web Apps with uWSGI and Nginx.

Conclusion

If you followed everything, you should understand how to install nginx and configure it for some common use cases. You should know where to go to find more information and configuration options (nginx.org documentation). You should feel comfortable setting up nginx as a static http web server, using it as a reverse proxy, setting up redirects, and adding SSL. If you want to learn more, explore creating custom modules for nginx, using the load balancing and rate limiting features, proxying non-http traffic, and look at some of the other configuration options available like logging, gzip, custom headers, etc.

Advertisement

Advertisement