NGINX Plus looks great :)

Basic

Config file

  • location: /etc/nginx
  • main: /etc/nginx/nginx.conf
  • include: /etc/nginx/conf.d/*.conf

basic config:

server {
    listen 80 default_server;  # port, default_server means all the traffic and ignore server_name
    server_name www.example.com;  # only match the request for this server_name
    
    location / {  # all traffic
        root /usr/share/nginx/html;  # file location
        index index.html index.htm;  # default file
    }
}
  • test config: nginx -t
  • graceful reload: nginx -s reload

High performance

Load balance

upstream backend {
    least_conn;  # load balancing method
    server 10.0.0.1:80 weight=1 max_fails=3 fail_timeout=3s;
    server app.example.com:80 weight=2;
}

server {
    location / {
        listen 80;
        proxy_pass backend;
        health_check interval=10 passes=2 fails=3;
    }
}

Load balancing methods:

  • Round Robin
  • Least connection
  • Least time (plus)
  • Generic hash
  • IP hash
  • Random

Traffic management

A/B testing

split_clients "${remote_addr}AAA" $variant {
               20.0%              "backendv2";
               *                  "backendv1";
}

server {
    ...
    location / {
        ...
        proxy_pass http://api.com/$variant
    }
}

Limiting connections

http {
    limit_conn_zone $binary_remote_addr zone=limitbyaddr:10m;
    limit_conn_status 429;
    ...
    server {
        ...
        limit_conn limitbyaddr 40;  # limit_zone & allowed number
        ...
    }
}

Limiting rate

http {
    limit_req_zone $binary_remote_addr zone=limitbyaddr:10m rate=1r/s;
    limit_req_status 429;
    ...
    server {
        ...
        limit_req zone=limitbyaddr burst=10 nodelay;
        ...
    }
}

Limiting bandwidth

This limit is per connection.

location /download/ {
    limit_rate_after 10m;
    limit_rate 1m;
}

Massive scalable content caching

proxy_cache_path /var/nginx/cache
                 keys_zone=CACHE:60m
                 levels=1:2
                 inactive=3h
                 max_size=20g;
server {
    ...
    proxy_cache_key "$host$request_uri $cookie_user";  # default is $scheme$proxy_host$request_uri
    proxy_set_header Range $slice_range;
    proxy_http_version 1.1;
    proxy_cache_valid 200 206 1h;
    proxy_cache_bypass $http_cache_bypass;
    proxy_cache CACHE;
    slice 1m;  # for HTML5 video
    
    location / {
        proxy_pass http://origin:80;
    }

    location ~* \.(css|js)$ {
        expires 1y;
        add_header Cache-Control "public";
    }

Authentication

HTTP basic authentication

Provide a username-password file like:

name1:password1
name2:password2:comment

Both openssl and htpasswd can generate passwords with the apr1 algorithm, which can be understand by Nginx.

location / {
    auth_basic "Not allowed for unauthenticated users";
    auth_basic_user_file conf.d/passwd;
}

Authentication subrequest

location /private/ {
    auth_request /auth;
    auth_request_set $auth_status $upstream_status;
}

location = /auth {
    internal;
    proxy_pass http://auth-server;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
}

Security controls

Access based on IP address

location /admin/ {
    deny 10.0.0.1;
    allow 10.0.0.0/20;
    allow 2001:0db8::/32;
    deny all;
}

Allowing cross-origin resource sharing

map $request_method $cors_method {
    OPTIONS 11;
    GET 1;
    POST 1;
    default 0;
}

server {
    ...
    location / {
        if ($cors_method ~ '1') {
            add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
            add_header 'Access-Control-Allow-Origin' '*.example.com';
            add_header 'Access-Control-Allow-Headers'
                       'DNT,
                        Keep-Alive,
                        User-Agent,
                        X-Requested-With,
                        If-Modified-Since,
                        Cache-Control,
                        Content-Type';
        }
        if ($cors_method = '11') {
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Type' 'text/plain; charset=UTF-8';
            add_header 'Content-Length' 0;
            return 204;
        }
    }
}

Client-side encryption

http { # All directives used below are also valid in stream
    server {
    listen 8433 ssl;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_certificate /etc/nginx/ssl/example.pem;
    ssl_certificate_key /etc/nginx/ssl/example.key;
    ssl_certificate /etc/nginx/ssl/example.ecdsa.crt;
    ssl_certificate_key /etc/nginx/ssl/example.ecdsa.key;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    }
}

Elliptic Curve Cryptography(ECC) certifications are found to be faster than the equivalent-strength RSA certifications.

Upstream Encryption

location / {
    proxy_pass https://upstream.example.com;
    proxy_ssl_verify on;
    proxy_ssl_verify_depth 2;
    proxy_ssl_protocols TLSv1.2;
}

Securing a location

location /resources {
    secure_link_secret mySecret;
    if ($secure_link = "") { return 403; }
    
    rewrite ^ /secured/$secure_link;
}

location /secured/ {
    internal;
    root /var/www;
}

Securing a location with an expire date

location /resources {
    root /var/www;
    secure_link $arg_md5,$arg_expires;
    secure_link_md5 "$secure_link_expires$uri$remote_addr mySecret";
    if ($secure_link = "") { return 403; }
    if ($secure_link = 0) { return 410; }
}

HTTPS redirects

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://$host$request_uri;
}

Redirecting to HTTPS where SSL/TLS is terminated before Nginx

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    if ($http_x_forwarded_proto = 'http') {
        return 301 https://$host$request_uri;
    }
}

Satisfying any number of security methods

location / {
    satisfy any;
    allow 192.168.1.0/24;
    deny all;
    
    auth_basic "closed site";
    auth_basic_user_file conf/htpasswd;
}

HTTP/2

Basic configuration

server {
    listen 443 ssl http2 default_server;
    
    ssl_certificate server.crt;
    ssl_certificate_key server.key;
    
    # add gRPC
    location /service1 {
        grpc_pass grpc://backend.local:50051;  # unencrypted
    }
    location /service2 {
        grpc_pass grpcs://backend.local:50052;  # encrypted
    }
    ...
}

gRPC

server {
    listen 80 http2;  # unencrypted
    
    location / {
        grpc_pass grpc://backend.local:50051;
    }
}

HTTP/2 server push

server {
    listen 443 ssl http2 default_server;
    
    ssl_certificate server.crt;
    ssl_certificate_key server.key;
    root /usr/share/nginx/html;
    
    location = /demo.html {
        http2_push /style.css;
        http2_push /image1.jpg;
    }
}

Sophisticated media streaming

Serving MP4 and FLV

http {
    server {
        ...
        location /videos/ {  # files in this directory are in MP4 format type
            mp4;
        }
        location ~ \.flv$ {  # any files ending in .flv are in FLV format
            flv;
        }
    }
}

Containers/Microservices

Official NGINX image

docker run --name my-nginx -p 80:80 -v /path/to/content:/usr/share/nginx/html:ro -d nginx

This maps locolhost:80 to port 80 of the container, and mounts the local directory /path/to/content as a container volume at /usr/share/nginx/html as read only.

Creating an NGINX Dockerfile

FROM centos:7

RUN yum -y install epel-release && \
    yum -y install nginx
    
ADD /nginx-conf /etc/nginx

EXPOSE 80 443

CMD ['nginx']

Use environment variables in NGINX

daemon off;
env APP_NDS;
include /usr/share/nginx/modules/*.conf;
...
http {
    perl_set $upstream_app 'sub { return $ENV{"APP_DNS"}; }';
    server {
        ...
        location / {
            proxy_pass https://$upstream_app;
        }
    }
}

To use perl_set you must have the ngx_http_perl_module installed. (yum -y install nginx nginx-mod-http-perl)

Advanced activity monitoring

Enable NGINX Open Source stub status

location /stub_status {
    stub_status;
    allow 127.0.0.1;
    deny all;
}

Debuggin and troubleshooting with access logs, error logs, and request tracing

Configuring access logs and error logs

http {
    log_format geoproxy
               '[$time_local] $remote_addr '
               '$realip_remote_addr $remote_user '
               '$request_method $server_protocol '
               '$schema $server_name $uri $status '
               '$request_time $body_bytes_sent '
               '$geoip_city_country_code3 $geoip_region '
               '"$geoip_city" $http_x_forwarded_for '
               '$upstream_status $upstream_response_time '
               '"$http_referer" "$http_user_agent"';
    ...
    server {
        access_log /var/log/nginx/access.log geoproxy;
        error_log /var/log/nginx/error.log warn;
        ...
    }
}

Forwarding to syslog

error_log syslog:server=10.0.0.1 debug;
access_log syslog:server=10.0.0.1,tag=nginx,severity=info geoproxy;

A common log aggregation stack is ElasticSearch, Logstash, and Kibana(ELK stack).

Request tracing

Using request_id to track request. It will generate string of 32 hex characters.

log_format trace '$remote_addr - $remote_user [$time_local] '
                 '"$request" $status $body_bytes_sent '
                 '"$http_referer" "$http_user_agent" '
                 '"$http_x_forwarded_for" $request_id';
upstream backend {
    server 10.0.0.1;
}
server {
    listen 80;
    add_header X-Request-ID $request_id;
    location / {
        proxy_pass http://backend;
        proxy_set_header X-Request_ID $request_id;
        access_log /var/log/nginx/access_trace.log trace;
    }
}

Performance tuning

Keeping connections open to clients

http {
    keepalive_requests 320;  # default 100
    keepalive_timeout 300s;  # default 75
    ...
}

Keeping connections open upstream

proxy_http_version 1.1;
proxy_set_header Connection "";

upstream backend {
    server 10.0.0.1;
    server 10.0.0.2;
    
    keepalive 32;
}

Buffering responses

server {
    proxy_buffering on;
    proxy_buffer_size 8k;
    proxy_buffers 8 32;
    proxy_busy_buffer_size 64k;
    ...
}

Buffering access log

http {
    access_log /var/log/nginx/access.log main buffer=32k flush=1m;  # flush the log older than 1 minute
}

OS tuning

  • connection number: net.core.osmaxconn
  • open file descriptors: sys.fs.file_max, worker_connections, worker_rlimit_nofile
  • enable more ephemeral ports: net.ipv4.ip_local_port_range