Nginx Common Configurations

Reverse Proxy

HTTP

http {
...
server {
listen 80;
server_name myserver.com;
# The default root is /usr/share/nginx/www, /usr/share/nginx/html or /var/www/html
root /var/www/your_domain/html;

# Staic files
location / {
# redefine the root
root /var/www/your_domain/html;
try_files $uri $uri/index.html =404;
# if not found redirect to home page $root/index.html
# try_files $uri $uri/ /index.html;
}

# API
location /api/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# The URL of the back-end API has the /api prefix. https://example.com/api/xxx to https://api.example.com/api/xxx.
proxy_pass http://localhost:8080/api/;
# The URL of the back-end API does not have the /api prefix. The /api prefix is only used to map to the API interface. https://example.com/api/xxx to https://api.example.com/xxx.
proxy_pass http://localhost:8080/;
}

# Cache js, css, image, etc.
}
...
}
  • Response static files: {root path}/requestURI
  • Proxy requests: {proxy_pass path}/requestURI

Passing Request Headers

By default, NGINX redefines two header fields in proxied requests, “Host” and “Connection”, and eliminates the header fields whose values are empty strings. “Host” is set to the $proxy_host variable, and “Connection” is set to close.

HTTPS

http {
# reuse SSL session parameters to avoid SSL handshakes for parallel and subsequent connections.
# or "ssl_session_cache builtin:1000 shared:SSL:10m;"
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

server {
listen 443 ssl;
server_name myproject.com;

ssl_certificate /etc/ssl/projectName/projectName.com.pem;
ssl_certificate_key /etc/ssl/projectName/projectName.com.key;

# Additional SSL configuration (if required)
# enabling keepalive connections to send several requests via one connection and the second is to reuse SSL session parameters to avoid SSL handshakes for parallel and subsequent connections.
keepalive_timeout 70;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;

# same with HTTP
location / {
...
}
}
...
}

HTTP to HTTPS

http {
...
server {
listen 80;
server_name myserver.com;
return 301 https://myserver.com$request_uri;
}
...
}

Optimization

Enable HTTP2

The ngx_http_v2_module module (1.9.5) provides support for HTTP/2. This module is not built by default, it should be enabled with the --with-http_v2_module configuration parameter.

To enable HTTP/2, you must first enable SSL/TLS on your website. HTTP/2 requires the use of SSL/TLS encryption, which provides a secure connection between the web server and the client’s browser.

http {
server {
# enable http2
listen 443 ssl http2;

ssl_certificate server.crt;
ssl_certificate_key server.key;
}
}

Cache static files

When you build static assets with versioning/hashing mechanisms, adding a version/hash to the filename or query string is a good way to manage caching. In such a case, you can add a long max-age value and immutable because the content will never change.

http {
server {
...
location /static {
root /var/www/your_domain/staic;
# To disable access log off for not hitting the I/O limit
access_log off;
# or "expires max";
expires 7d;
# build static assets with versioning/hashing mechanisms
add_header Cache-Control "public, immutable";
# revalidate
# add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
...
}
}
http {
server {
...
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico)$ {
root /var/www/your_domain/staic;
# To disable access log off for not hitting the I/O limit
access_log off;
# or "expires max";
expires 7d;
# build static assets with versioning/hashing mechanisms
add_header Cache-Control "public, immutable";
# revalidate
# add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
...
}
}

After setting cache, the following headers will be present in the response headers:

Cache-Control: max-age=604800, public, must-revalidate, proxy-revalidate

Warning: Cache-Control "public, immutable" for JS, CSS may cause program exceptions after update program.

Compression

Enable gzip

http {
...
server {
...
# gzip can be set in `http {}` or `server {}`
# --with-http_gzip_static_module
gzip on;
# By default, NGINX compresses responses only with MIME type text/html. To compress responses with other MIME types, include the gzip_types directive and list the additional types.
gzip_types text/css text/xml application/javascript;
# To specify the minimum length of the response to compress, use the gzip_min_length directive. The default is 20 bytes
gzip_min_length 200;
# Sets the number and size of buffers used to compress a response.
gzip_buffers 32 4k;
# Sets a gzip compression level of a response. Acceptable values are in the range from 1 to 9.
gzip_comp_level 6;
gzip_vary on;
...
}
}

After enable gzip, the following headers will be present in the response headers:

content-encoding: gzip

Settings

Timeout

http {
...
# proxy_connect_timeout default 60s
proxy_connect_timeout 180s;
# proxy_send_timeout default 60s
proxy_send_timeout 180s;
# proxy_read_timeout default 60s
proxy_read_timeout 180s;
...
}

Upload File Size

http {
...
# client_max_body_size default 1M
client_max_body_size 100M;
...
}

Enable CORS for API

Enable CORS for specified sites

http {
...

map "$http_origin" $cors {
default '';
"~^https?://localhost(:\d+)?$" "$http_origin";
"~^https?://10.0.0\.\d{1,2}(:\d+)?$" "$http_origin";
"~^https?://example\.com$" "$http_origin";
}

server {
# API
location /api/ {
...

# Attach CORS headers only if it's a valid origin ($cors should not be empty)
if ($cors != "") {
# using $cors for specified sites or using $http_origin for any sites.
proxy_hide_header 'Access-Control-Allow-Origin';
add_header 'Access-Control-Allow-Origin' '$cors' always;
proxy_hide_header 'Access-Control-Allow-Credentials';
add_header 'Access-Control-Allow-Credentials' true always;
proxy_hide_header 'Access-Control-Allow-Methods';
add_header 'Access-Control-Allow-Methods' 'POST, GET, DELETE, PUT, PATCH' always;
proxy_hide_header 'Access-Control-Allow-Headers';
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
}
# Check if it's a preflight request and "cache" it for 20 days
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '$cors' always;
add_header 'Access-Control-Allow-Credentials' true always;
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}

...
}
}
}

Enable CORS for all sites

http {
...

server {
# API
location /api/ {
...

proxy_hide_header 'Access-Control-Allow-Origin';
add_header 'Access-Control-Allow-Origin' '$http_origin' always;
proxy_hide_header 'Access-Control-Allow-Credentials';
add_header 'Access-Control-Allow-Credentials' true always;
proxy_hide_header 'Access-Control-Allow-Methods';
add_header 'Access-Control-Allow-Methods' 'POST, GET, DELETE, PUT, PATCH' always;
proxy_hide_header 'Access-Control-Allow-Headers';
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;

# Check if it's a preflight request and "cache" it for 20 days
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '$http_origin' always;
add_header 'Access-Control-Allow-Credentials' true always;
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}

...
}
}
}

Load Balancing

http {
...
upstream backend-server {
server xxx.xxx.xxx.xxx:8080 max_fails=1 fail_timeout=300s;
server xxx.xxx.xxx.xxx:8080 max_fails=1 fail_timeout=300s;
...
}

server {
...
location /api/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://backend-server/;
}
}
}

Test the Nginx Configuration is Updated

Adding the following config to the Nginx configuration file. You can verify if the configuration is updated by updating the return status code (e.g. 403 Forbidden, 406 Not Acceptable, 423 Locked) of the /test location and visiting the test URL http://yourDomain/testConfig.

location /testConfig {
# 403 Forbidden, 406 Not Acceptable, 423 Locked
return 403;
}

Appendixes

Embedded Variables

  • $proxy_host: name and port of a proxied server as specified in the proxy_pass directive;
  • $proxy_add_x_forwarded_for: the “X-Forwarded-For” client request header field with the $remote_addr variable appended to it, separated by a comma. If the “X-Forwarded-For” field is not present in the client request header, the $proxy_add_x_forwarded_for variable is equal to the $remote_addr variable.
  • $host: In this order of precedence: host name from the request line, or host name from the “Host” request header field, or the server name matching a request.
  • $remote_addr: Client address

Build Nginx From Source

# Download Nginx source code
wget http://nginx.org/download/nginx-{latest-stable-version}.tar.gz

You can know the latest version of Nginx by visiting the Nginx download page.

tar -zxvf nginx-{latest-stable-version}.tar.gz

cd nginx-{latest-stable-version}

# Configuring Nginx
./configure \
--with-pcre \
--with-http_ssl_module \
--with-http_image_filter_module=dynamic \
--modules-path=/etc/nginx/modules \
--with-http_v2_module \
--with-stream=dynamic \
--with-http_addition_module \
--with-http_mp4_module \
--with-http_gzip_static_module

More configuration

Common errors when running ./configure

1. ./configure: error: the HTTP rewrite module requires the PCRE library.

Solution

sudo apt update && apt upgrade -y && apt autoremove && apt autoclean
apt-get install libpcre3 libpcre3-dev
# or
sudo yum update -y && yum upgrade && yum autoremove
yum install libpcre3 libpcre3-dev
yum -y install pcre-devel openssl openssl-devel

2. ./configure: error: the HTTP image filter module requires the GD library. You can either do not enable the module or install the libraries.

Solution

sudo apt update && apt upgrade -y && apt autoremove && apt autoclean
apt-get install gd-devel
# or
sudo yum update -y && yum upgrade && yum autoremove
yum install gd-devel -y

3. ./configure: error: C compiler cc is not found

Solution

sudo apt update && apt upgrade -y && apt autoremove && apt autoclean
sudo apt-get install linux-kernel-headers build-essential -y
# or
sudo yum update -y && yum upgrade && yum autoremove
sudo yum install gcc -y

Successful output of configure

Configuration summary
+ using system PCRE2 library
+ using system OpenSSL library
+ using system zlib library

nginx path prefix: "/usr/local/nginx"
nginx binary file: "/usr/local/nginx/sbin/nginx"
nginx modules path: "/etc/nginx/modules"
nginx configuration prefix: "/usr/local/nginx/conf"
nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
nginx pid file: "/usr/local/nginx/logs/nginx.pid"
nginx error log file: "/usr/local/nginx/logs/error.log"
nginx http access log file: "/usr/local/nginx/logs/access.log"
nginx http client request body temporary files: "client_body_temp"
nginx http proxy temporary files: "proxy_temp"
nginx http fastcgi temporary files: "fastcgi_temp"
nginx http uwsgi temporary files: "uwsgi_temp"
nginx http scgi temporary files: "scgi_temp"

You can add the following parameters to specify paths:

--prefix=/var/www/html \
--sbin-path=/usr/sbin/nginx \
--modules-path=/etc/nginx/modules \
--conf-path=/etc/nginx/nginx.conf \
--pid-path=/var/run/nginx.pid \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--lock-path=/var/lock/nginx.lock \
# Build nginx
$ make
$ sudo make install

Successful output of build

make -f objs/Makefile install
make[1]: 进入目录“/root/yuqing/nginx-1.26.1”
...
# make[1]: 离开目录“/root/yuqing/nginx-1.26.1”
# Start nginx
$ cd /usr/local/nginx/sbin
$ ./nginx -V
$ ./nginx
# Verify
$ curl http://localhost

Rebuild Nginx source

# Just remove the nginx binary file. Or completely remove nginx `sudo apt-get purge nginx` or `yum remove package`
cd /usr/local/nginx/sbin
mv nginx nginx.bak

# configure
tar -zxvf nginx-{latest-stable-version}.tar.gz
cd nginx-{latest-stable-version}
# Configuring Nginx
./configure ...

# Build and install nginx
$ make
$ sudo make install

References

[1] Configuring HTTPS servers - Nginx

[2] Alphabetical index of variables - Nginx

[3] Serving Static Content - Nginx

[4] NGINX Reverse Proxy - Nginx