Nginx Cookbook
Interested in using Nginx, the most popular web server worldwide(probably true) for your self-hosted services? And not
so interested in reading the boring documentation? Then this is the one-stop guide for you. I’ve written this for
future references and others who want to use Nginx for small-scale application deployment.
Before we start
I’m expecting you have a development/deployment environment running Linux. For installation, there should be
prebuilt binaries for most Linux distros available. You should follow
the official guide for this. After installation,
you should be able to find the configuration named nginx.conf
in one of these directories: /etc/nginx
, /usr/local/nginx/conf
, or /usr/local/etc/nginx
. A default installation comes with examples inside the
configuration directory. You could start from scratch and remove nginx.conf
.
Signals
You can use the following command to send a signal to Nginx. They are pretty self-explanatory. Note: This is not the complete list. See Controlling nginx on the official site.
nginx -s (stop|quit|reload)
For example, after editing the configuration file, you should execute nginx -s reload
for Nginx to read your changes.
Configuration Format
# context: global
events {
worker_connections 1024;
}
http {
# block name: http, context: http
server {
#block name: server, context: http->server
listen 80;
server_name example.com;
location / {
#block name: location, context: http->server->location
#this is a directive.
root /www/data;
}
}
}
The lines starting with # are comments. Note the space between the block name and the braces.
If you’ve learned OOP before, you should be already familiar with this kind of structure. If not, each block has a name,
and its body surrounded by curly brackets. The content of a block would only affect this block, and its children(
inner-blocks). Anything that doesn’t belong to a block are in the main
(global) context.
A directive is a command or a configuration entry, in the format of:
name param1 param2 paramN;
Notice the semicolon at the end of the line, it’s required at the end of a directive or else Nginx will scream at you. If you like to have the directives aligned properly, you could optionally use more than one spaces between parts.
Events Block
The events
block is always required. It should reside in the main
context, so make sure you don’t put it inside any
other blocks. I’ll recommend you to copy this part as is if you don’t fully understand what you’re doing.
events {
worker_connections 1024;
}
Servers Block
By defining a server
block, Nginx will create a virtual server for you, which will be handling your web traffic. You
could the following directives to apply different filters to the traffic, after which this server will process.
listen
This server will listen on port 80.
listen 80;
If you have multiple servers on the same port, requests that doesn’t match any other server will be routed to this one:
listen 80 default_server;
server_name
One or more entries should be specified here. You can use IP addresses, domains, etc. The Host header will be matched against each item in your list. The server names can include an asterisk(*) replacing the first or last part of a name. And you can also use regular expressions, which should be proceeded with a tilde(~).
server_name example.com;
server_name *.example.com;
server_name example.*;
server_name ~^www\d+\.example\.com$;
Recipes
Serving Static Files
server {
location / {
root /data/www;
}
# also use regex
location ~* \.(gif|jpg|png)$ {
root /data/www/imgs;
}
}
The path of a request: example.org/this/is/the/path.png?not=included
It’s that simple. The location block will match the path of the request. Then the path is appended to the value of
your root
directive. If the file exists, it returns the file with code 200 OK. If not, code 404 NOT_FOUND is returned.
When a request matches more than one location blocks, the most specific prefix location is used. This means /a/b
prioritised instead of /a
Custom error pages
server {
error_page 404 /www/error/404.html
}
Replace 404 with any error codes that you would like to serve a custom error page. Need a list?
Reverse Proxy
A reverse proxy is helpful if you want to build a simple API gateway, or when you want to add SSL to an application using HTTP(covered in recipe “SSL”).
server {
location / {
proxy_pass http://localhost:8080;
}
}
Similar to the root
directive mentioned above, proxy_pass
works the same way. The path of the request will be
appended to your server of choice, and nginx will request that URL and pass the response back to the client(this is
called reverse proxy).
Setting Headers for Origin
proxy_set_header Content-Type "application/json";
Rewrite headers for the request sent to the origin.
Origin: the real web server behind your proxy server(which is Nginx).
Rate Limit/Speed Limit
Note: This only limits the speed of reading the response from the proxied server. Look below if you want to set up per-client rate limiting.
proxy_limit_rate 1k;
The rate(1k=1024) here is specified in bytes per second(Bps), not to be confused with bits per second(bps).
Authentication
location / {
allow 192.168.1.1;
auth_basic "wrong password";
auth_basic_user_file conf/htpasswd;
satisfy any;
}
Multiple authentication methods could be used for one block. Use the satisfy
directive to declare whether to
apply any
or all
authentication methods for a request. I hope that makes sense.
The directives shown in this recipe could all be used in http
, location
and server
blocks.
IP Address Filter
You could deny all
access to your services and only allow <your ip>
.
allow 192.168.1.1;
deny 1.1.1.1;
# also CIDRs
allow 192.168.8.1/24;
# and also "all"
deny all;
Basic Auth
# a authentication realm is specified here. Read more: https://stackoverflow.com/questions/12701085/what-is-the-realm-in-basic-authentication
auth_basic "my realm";
auth_basic_user_file conf/htpasswd;
A file should be specified for auth_basic_user_file
in the following format:
name1:password1
name2:password2:comment
name3:password3
The password could be generated with the command openssl passwd
. The result is hashed, so no need to worry about
saving them on your drive.
JWTs
This is a good option if you have an SSO or similar set up on the same domain. It’s too complicated for this tutorial so please refer to the official document.
Rate Limiting
If you wish to protect your services from bots and scrapers, something like fail2ban or a WAF could be more useful. They provide much granular rate limit strategies.
This recipe skips some of the arguments that’s not commonly used for normal users. Refer to the official docs for more details.
limit_req_zone
Nginx uses the “leaky bucket” method to determine which client should be
rate limited. Each key
(client) gets a bucket that has a set capacity. Each request will fill up that bucket by a bit.
When the bucket overflows, the client is rate limited and an error will be returned. The bucket also leaks at a fixed
rate, which enables you to control the average flow of requests and allow bursts when the client is in need.
limit_req_zone
is used to define a zone. It should only be used in the http
context.
limit_req_zone key zone=name:size rate=rate;
limit_req_zone $binary_remote_addr zone=auth_zone:10m rate=3r/m;
$binary_remote_addr
here is used as the key. It’s a variable which would be replaced by the byte representation of the
client IP address. Variables will be explained in a later chapter.
auth_zone
here should be replaced by your zone name, and 10m
means 10 megabytes of memory will be used to store the states of the zone. You
shouldn’t set this to a value too small, nor too big. 1 megabytes could store up to 8k states(8000 different clients in
this case).
rate
is specified in requests per second(r/s), or requests per minute(r/m).
limit_req
server {
# also in the server context
limit_req zone=my_service;
location /login {
# use different rules for different contexts
limit_req zone=my_service_auth;
}
}
SSL/HTTPS
If you haven’t got an SSL certificate ready, you should consider Lets Encrypt, it’s free. Install and run Certbot to get free HTTPS certificates automatically renewed forever( technically).
After acquiring the public and private keys, add the following directives to your http
block.
http {
listen 443 ssl;
ssl_certificate /path/to/public_key.pem;
ssl_certificate_key /path/to/private_key.pem;
}
Don’t forget to tell Nginx to listen
on port 443 for SSL traffic.
If you have Certbot installed, use this command to have Certbot edit your Nginx configuration automatically.
sudo certbot --nginx
Logging
Use the default log format and a custom log file:
access_log /path/to/log.log;
For advanced formatting and performance tuning, please refer to the official docs.
PHP via FastCGI
server {
location / {
include snippets/fastcgi-php.conf;
fastcgi_pass /var/run/php-fpm.sock;
}
}
A default Nginx installation should provide you with $nginx_root$/snippets/fastcgi-php.conf
which also
includes $nginx_root$/fastcgi.conf
. These two files has some boilerplate config that you only need to include
for
PHP. Note only PHP is supported here, so if you’re using something else, you’ll have to configure
the ngx_http_fastcgi_module
module by yourself.
fastcgi_pass
works just like proxy_pass
which tells Nginx to pass the request to the FastGCI processor.
Afterwords
And that’s it. Hope you now have some idea about how to config Nginx to fit your own needs. Thanks for reading.
Continue reading: Nginx Full Example Configuration