Nginx by examples: the basics
Nginx is a very popular http server/rev proxy and can be used in multiple configurations. Knowing how to configure it can literally save your day!
Getting Started
To start with let’s install the latest version of Nginx from the official Nginx PPA (under Ubuntu)
sudo -s
add-apt-repository ppa:nginx/stable
apt-get update
apt-get install nginx
Basic conf
Once it’s installed, Nginx comes with the following configuration (that I’ve slightly tweaked):
# runs worker processes nginx as www-data
user www-data;
# sets the number of worker processes to the number of cores as automatically identified by nginx at sturtup
worker_processes auto;
pid /run/nginx.pid;
events {
# the number of incoming and outgoing connections per worker
worker_connections 768;
}
http {
# nginx core optimizations to serve files efficiently
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# the keepalive on http/1.1 connections
keepalive_timeout 65;
# internal parameter to speed up hashtable lookups
types_hash_max_size 2048;
# sends the nginx version in the server header
# server_tokens off;
# default mime type mapping (from extensions)
include /etc/nginx/mime.types;
# mime default if the previous mapping fails
default_type application/octet-stream;
# SSL settings
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
# default logs
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# turns GZIP on but only for text/html mime types by default
gzip on;
gzip_disable "msie6";
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# additional configuration
include /etc/nginx/conf.d/*.conf;
# Virtual Host Configs
include /etc/nginx/sites-enabled/*;
}
First look
The first thing to notice is that nginx directives tend to be very expressive and a small change can make a big difference. This makes the config files very concise (as opposed to Apache’s)
Secondly the directives can be expressed in any order and they are evaluated all together. Therefore two contradicting statements are not allowed (like with Apache).
Nginx processes
Nginx is an event based web server. For maximum performance the number of processes should always be equal to the number of cores available as each process affinity is automatically adjusted to match each core with only one Nginx process.
Setting worker_processes
to something higher than the number of cores is useless and does result in performance degradation
sendfile, tcp_nopush, tcp_nodelay
One of the reasons nginx got so popular so quickly is the ability to serve static content very fast and at scale. The original secret behind it sits with these 3 directives. If you serve a lot of static files the default values work just fine. If you serve primarily dynamic content things can be tweaked a bit.
Gzip
By default the Gzip configuration is limited to gzipping only text/html
content types. Also the default compression level 6
is too high and may cause too much stress on the CPU, limiting your throughput just to save little bandwidth.
If you are hosting on AWS consider bringing gzip_comp_level
down to 1 or 2 and Always uncomment the gzip_types
directive.
Also remember that compressing binary files like images and videos may make very little sense (as they are already compressed)
Virtual hosts
The default virtual host located at /etc/nginx/sites-available/default
looks something like:
server {
# sets this virtual host to listed on port 80 (ipv4 by default)
listen 80;
# sets the server name for virtual host resolution
server_name localhost;
# custom error and access logs location
error_log logs/host.error.log error;
access_log logs/host.access.log;
# the default location block
location / {
# the root directory to use to serve files
root /usr/share/nginx/html;
# tries to match the incoming URI with more than one possible file/directory on disk
try_files $uri $uri/index.html $uri.html =404;
# what files to serve when a directory is matched
index index.html index.htm;
}
# deny access to .htaccess files, if any
location ~ /\.ht {
deny all;
}
}
Server name and listen
The server_name
directive drives the virtual host resolution process and can be a source of headaches in case things are not configured properly.
It accepts multiple values in the form or simple strings, wildcards and regexes but unless you really know what you are doing it’s safe to stick to simple strings only
Please refrain from using:
server {
...
server_name _;
...
}
as it’s a no match value that should never be used. _
will never match any server name, hence Nginx will resolve ambiguous requests using the first server{}
block encountered which is most likely not what you wanted
The correct solution is to use
server {
...
listen 80 default_server;
...
}
Location block resolution
This is the trickiest bit of all to understand. When a request arrives Nginx tries to respond using the most appropriate location block based on multiple and somehow complex rules.
A location can either be defined by a prefix string, or by a regular expression. Regular expressions are specified with the preceding “~*” modifier (for case-insensitive matching), or the “~” modifier (for case-sensitive matching). To find location matching a given request, nginx first checks locations defined using the prefix strings (prefix locations). Among them, the location with the longest matching prefix is selected and remembered. Then regular expressions are checked, in the order of their appearance in the configuration file. The search of regular expressions terminates on the first match, and the corresponding configuration is used. If no match with a regular expression is found then the configuration of the prefix location remembered earlier is used.
If the longest matching prefix location has the “^~” modifier then regular expressions are not checked.
Let’s try and simplify:
- Nginx will try and match the longest URI based on the incoming request
- The
location / {}
block is the shortest URI possible and acts effectively as a default value when other blocks don’t match - Exact matches using
location = /some-uri
are very fast to process and save a significant amount of CPU cycles. As the home page of every domain is the most crawled page there should always be a block like
location = / {
# some other config here
}
to reduce the load on the server
- Don’t use a regex unless it’s strictly necessary (like with the PHP FCGI connector. Regexes when present take precedence over simple URI matching and can add significant computational overhead depending on their complexity.