Monday, June 25, 2018

Load Balancing in Nginx


Overview
--------

- Nginx can provide load balancing to a group of servers
- Advantages of load balancing
    a. optimize resource utilization
    b. maximize throughput
    c. reduce latency
    d. fault tolerance

Proxying Traffic to a group of servers
--------------------------------------

- setup is similar to `Secure TCP to Upstream` KB

example config
http {
    upstream backend {
        # `server` directive here is different from
        # the one used in virtual servers
        server backend1.example.com;

        # member 2
        server backend2.example.com;

        # backup
        server 192.0.0.1 backup;
    }
    server {
        location / {
            # call the upstream via name
            proxy_pass http://backend;
        }
    }
}
actual testing


# version

nginx version: nginx/1.10.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)
built with OpenSSL 1.0.1e-fips 11 Feb 2013

# proxy server config
 
[/etc/nginx/conf.d]_$ cat virt1.conf
upstream backend {
  server backend1.home.net;
  server backend2.home.net;
 }

server {
  server_name virt1.home.net;

  location / {
    proxy_pass http://backend;
  }
}
[/etc/nginx/conf.d]_$

# backend1 config
 
[/etc/nginx/conf.d]_$ cat backend1.conf
server {
  listen 192.168.1.11:80;
  server_name backend1.home.net;
  root /data;

  location / {
    autoindex on;
  }
}
[/etc/nginx/conf.d]_$

# backend2 config
 
[/etc/nginx/conf.d]_$ cat backend2.conf
server {
  listen 192.168.1.12:80;
  server_name backend2.home.net;
  root /data;

  location / {
    autoindex on;
  }
}
[/etc/nginx/conf.d]_$

Load Balancing Methods
----------------------

Round-Robin

 - requests are distributed evenly across servers
 - server weights are taken into consideration
 - default method (no directive for enabling it)
upstream backend {
   server backend1.example.com;
   server backend2.example.com;
}
least_conn

 - request is sent to server w/ least number of
   active connections
 - server weights are taken into consideration
upstream backend {
    least_conn;

    server backend1.example.com;
    server backend2.example.com;
}
ip_hash

 - destination of request is determine from client's
   ip (first 3 octets of IPV4 or whole IPV6)
 - guarantees that requests from same IP gets into
   same server unless it is not available
 - if server needs to be removed temporarily, it
   can be mark as `down` to preserve current hashing
   of client IPs (requests to that server will go
   to the next in the group)
upstream backend {
    ip_hash;

    server backend1.example.com;
    server backend2.example.com;
}

in case 1 server is down for maintenance:

 
upstream backend {
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com down;
}
hash

 - generic hash method
 - destination server is determined by user-defined
   key (text/variable/source IP:PORT)
 - `consistent` option enables "ketama" hash load
   balancing
 - requests will be evenly distributed across based
   on user-defined hash value
 - if a server is added/removed, only few keys will
   be remapped w/c minimizes cache misses
upstream backend {
    hash $request_uri consistent;

    server backend1.example.com;
    server backend2.example.com;
}
least_time

 - available on Nginx PLUS only
 - selects server w/ lowest average latency and
   least number of connections
 - lowest average latency is computed based on:
    a. header - time to receive 1st byte from server
    b. last_byte - time to receive full response
                   from server
upstream backend {
    least_time header;

    server backend1.example.com;
    server backend2.example.com;
}

Server Weights
--------------

- by default, Nginx distributes requests among servers according to their
  weights using round-robin method
- default weight is 1

example config from
upstream backend {

    # This member has the highest weight of 5 meaning 5 out of 6
    # requests will go this server.
    server backend1.example.com weight=5;

    # This member has a default weight of 1.
    server backend2.example.com;
   
    # this server doesn't receive request unless any of the servers
    # above goes down (weight=1)
    server 192.0.0.1 backup;
}

Server Slow Start
-----------------

- prevents a recently recovered server from overwhelming by connections w/c may
  timeout and cause the server to be marked as failed again
- this allows to gradually recover its weight from zero to nominal value
- this is done using `slow_start` directive
- note: if there is only 1 server in a group, the following directives will be
  ignored
    a. max_fails
    b. fail_timeout
    c. slow_start

config
upstream backend {
    server backend1.example.com slow_start=30s;
    server backend2.example.com;
    server 192.0.0.1 backup;
}

Enabling Session Persistence
----------------------------

- Nginx plus can identify user sessions and route requests to same upstream
  server
- Nginx plus supports 3 session persistence methods using `sticky` directive:
    1. sticky cookie
         - simplest session persistence method
         - adds session cookie to the first response from upstream
         - identifies server w/c has sent the response
         - when client issues next request, it will contain the cookie and Nginx
           will route it to the same upstream server
    2. sticky route
         - when client sends 1st request, a route is assigned to that client
         - all subsequent requests will be compared to `route` option
         - route information is taken from cookie/URI
    3. cookie learn
         - NGINX Plus finds session identifiers by inspecting requests and
           responses
         - NGINX Plus learns w/c upstream server corresponds to w/c session
           identifier
         - session identifiers are passed to HTTP cookie
         - if a request contains a session identifier already "learned", NGINX
           Plus forwards the request to the corresponding server
         - doesn't require keeping cookies on client side
         - all info is kept on memory-side in shared memory zone
         - most sophisticated way of storing persistence method

Sample configs:

sticky cookie
upstream backend {
    server backend1.example.com;
    server backend2.example.com;

    # srv_id  -> cookie name
    # expires -> time cookie will expire on client browser
    # domain  -> domain for w/c cookie is set (optional)
    # path    -> path for w/c cookie is set (optional)
    sticky cookie srv_id expires=1h domain=.example.com path=/;
}
sticky route
upstream backend {
    server backend1.example.com route=a;
    server backend2.example.com route=b;

    sticky route $route_cookie $route_uri;
}
cookie learn
upstream backend {
   # upstream servers creates session by setting "EXAMPLECOOKIE" in the response < I don't understand
   server backend1.example.com;
   server backend2.example.com;


   # create -> indicates how a new session is created (required)
   # lookup -> indicates how to search for existing sessions (required)
   # zone   -> shared memory zone where all cookies are kept
   sticky learn
       create=$upstream_cookie_examplecookie
       lookup=$cookie_examplecookie
       zone=client_sessions:1m
       timeout=1h;
   # sequence:
   #   1. new session is created from cookie "EXAMPLECOOKIE" sent by upstream server
   #   2. existing sessions are searched in cookie "EXAMPLECOOKIE"
}

Limiting number of connections
------------------------------

- requires NGINX Plus
- number of connections can be limited using `max_conns` parameter
- if `max_conns` has been reached, further connections can be placed in queue if
  `queue` directive is specified
- if `queue` has been filled up or upstream server cannot be selected during the
  timeout period specified in `timeout`, client will receive an error
- NOTE: `max_conns` will be ignored if idle keepalive is set on worker processes
- number of connections can exceed `max_conns` in a configuration where the
  memory is shared w/ multiple worker processes

sample config
upstream backend {
    server backend1.example.com  max_conns=3;
    server backend2.example.com;

    queue 100 timeout=70;
}

Passive Health Monitoring
-------------------------

- when Nginx consider a server unavailable, it stops sending requests to that
  server until its consider to be active again
- `server` options w/c consider a server unavailable:
    a. fail_timeout (default: 10 seconds)
    b. max_fails (default: 1 attempt)

sample config
upstream backend {               
    server backend1.example.com;

    # server is mark unavailable for 30 seconds if response is not receive after 3 attempts
    server backend2.example.com max_fails=3 fail_timeout=30s;

    # server is mark unavailable for 10 seconds if response is not receive after 2 attempts
    server backend3.example.com max_fails=2;
}

Active Health Monitoring
------------------------

- a more sohpisticated way of monitoring compared to "Passive Health Monitoring"
- periodically sends special requests to servers and checks their response
- to activate: include `health_check` within the `location` block that passes
  request to the group

sample config
http {
    upstream backend {
       
        # shared memory zone among worker processes
        #   - stores configuration of server group
        #   - enables workers to use same set of counters to keep track
        #     of responses from servers in the group
        #   - a counter includes:
        #       a. current number of connections to each server in the group
        #   -   b. number of failed attempts to pass request to a server
        zone backend 64k;

        server backend1.example.com;
        server backend2.example.com;
        server backend3.example.com;
        server backend4.example.com;
    }

    server {
        location / {
  # - by default, Nginx Plus sends `/` requests to each member of group every 5 seconds
  # - if an error/timeout occured (proxied server respond other than status code 2XX/3XX),
  #   health check failed for that server
  # - a server that fails a health check is unhealty and Nginx Plus stops sending requests
  #   to it until it passes a healtch check
            proxy_pass http://backend;
            health_check;
        }
    }
}
default behaviour can be
overwritten using some parameters
in `health_check` directive
location / {
    proxy_pass http://backend;
    health_check interval=10 fails=3 passes=2;
    # interval - interval between checks in seconds
    # fails    - # of failed health checks to consider a server unhealthy
    # passes   - # of successful health checks to consider a server healthy
}
setting a specific URI to check
every health check
location / {
    proxy_pass http://backend;
    health_check uri=/some/path;
    # every health check will append the uri to the IP/hostname of
    # each server in the group
    # e.g
}
specifying a custom condition
that will satisfy a healthy
check using `match` directive
http {
    ...

    match server_ok {
        status 200-399;  # responses must be any number between 200 and 399
        body !~ "maintenance mode";  # body must not contain any kind of "maintenance mode" string (regex)
    }

    server {
        ...

        location / {
            proxy_pass http://backend;
            health_check match=server_ok;
        }
    }
}
another examples using `match`
# example 1
match welcome {
    status 200;
    header Content-Type = text/html;
    body ~ "Welcome to nginx!";
}

# example 2
match not_redirect {
    status ! 301-303 307;
    header ! Refresh;
}

Sharing Data with Multiple Worker Processes
-------------------------------------------

- if `upstream` directive doesn't include `zone` directive:
    a. each worker process keeps its own copy of server group config
    b. each maintain its own set of related counters
    c. server group configuration isn't changeable
- if `upstream` directive does include `zone` directive:
    a. server group config is placed in memory area shared among all workers
    b. workers utilize same set of counters
    c. dynamically configurable
- `zone` directive is mandatory for:
    a. health checks
    b. on-the-fly configurations

example scenario if server configuration
is not shared
For example, if the configuration of a group is not shared, each worker process maintains its
own counter for failed attempts to pass a request to a server (see the max_fails parameter).
In this case, each request gets to only one worker process. When the worker process that is
selected to process a request fails to transmit the request to a server, other worker
processes don’t know anything about it. While some worker process can consider a server
unavailable, others may still send requests to this server. For a server to be definitively
considered unavailable, max_fails multiplied by the number of workers processes of failed
attempts should happen within the timeframe set by fail_timeout. On the other hand, the
zone directive guarantees the expected behavior.
some notes on `least_conn` directive
The least_conn load balancing method might not work as expected without the zone directive,
at least on small loads. This method of tcp and http load balancing passes a request to the
server with the least number of active connections. Again, if the configuration of the group
is not shared, each worker process uses its own counter for the number of connections. And
if one worker process passes by a request to a server, the other worker process can also
pass a request to the same server. However, you can increase the number of requests to
reduce this effect. On high loads requests are distributed among worker processes evenly,
and the least_conn load balancing method works as expected.
setting the size of zone

 - there are no exact settings
 - it all depends on usage patterns
 - each feature such as:
     `sticky`
     `cookie`
     `route`
     `learn`
   will affect the zone size.
For example, the 256 Kb zone with the sticky_route session persistence method and a single
health check can hold up to:

128 servers (adding a single peer by specifying IP:port);
88 servers (adding a single peer by specifying hostname:port, hostname resolves to single IP);
12 servers (adding multiple peers by specifying hostname:port, hostname resolves to many IPs).

Configuring Load Balancing using DNS
------------------------------------

- Nginx Plus can monitor changes in server IP and apply this w/o restart
- this is done via `resolver` directive
- by default, Nginx resolves DNS records based on TTL
    > this can be overwritten using `valid` parameter
- if hostname resolves to multiple IPs, the IPs will be store on the configuration and will be load balanced

sample config
http {
    resolver 10.0.0.1 valid=300s ipv6=off;
    resolver_timeout 10s;

    server {
        location / {
            proxy_pass http://backend;
        }
    }
  
    upstream backend {
        zone backend 32k;
        least_conn;
        ...
        server backend1.example.com resolve;
        server backend2.example.com resolve;
    }
}

Load Balancing of Microsoft Exchange Servers
--------------------------------------------

- in release 7 and later, Nginx Plus can proxy traffic to Exchange servers and
  load balanced it

sample config
http {
    ...
    upstream exchange {
        zone exchange 64k;
        ntlm;
        server exchange1.example.com;
        server exchange2.example.com;
    }

    server {
        listen              443 ssl;
        ssl_certificate     /etc/nginx/ssl/company.com.crt;
        ssl_certificate_key /etc/nginx/ssl/company.com.key;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;

        location / {
            proxy_pass         https://exchange;
            proxy_http_version 1.1;
            proxy_set_header   Connection "";
        }
    }

On-Fly Configuration
--------------------

- In Nginx Plus, it is possible to modify load balancing configuration
    > view all servers
    > view a server in a group
    > modify parameter for a particular server
    > add server(s)
    > remove server(s)
- stores configuration in shared memory
- changes are discarded every time Nginx config file is reloaded

setup
http {
    ...
    # Configuration of the server group
    upstream appservers {
        zone appservers 64k;

        server appserv1.example.com      weight=5;
        server appserv2.example.com:8080 fail_timeout=5s;

        server reserve1.example.com:8080 backup;
        server reserve2.example.com:8080 backup;
    }

    server {
        # Location that proxies requests to the group
        location / {
            proxy_pass http://appservers;
            health_check;
        }

        # This allows on-the-fly configuration via API interface
        location /upstream_conf {
            upstream_conf;
            allow 127.0.0.1;
            deny  all;
        }
    }
}

Configuring Persistence of On-the-Fly Configuration
---------------------------------------------------

- changes is stored on a special file to avoid being discarded every config reload

setup
http {
    ...
    upstream appservers {
        zone appservers 64k;
        # This file is modified by configuration commands from `upstream_conf`
        # API interface. You should avoid modifying this file directly.
        state /var/lib/nginx/state/appservers.conf;

        # All these servers should be moved to the file using the upstream_conf API:
        # server appserv1.example.com      weight=5;
        # server appserv2.example.com:8080 fail_timeout=5s;
        # server reserve1.example.com:8080 backup;
        # server reserve2.example.com:8080 backup;
    }
}

APIs used to configure Upstream servers on the fly
--------------------------------------------------


Sunday, June 24, 2018

Nginx Content Caching


Introduction
------------

- if caching is enabled, Nginx saves responses to disk cache and uses them to
  respond to clients w/o having to proxy requests for the same content every
  time

Enabling Caching Response
-------------------------

enables caching
http {
    ...
    proxy_cache_path /data/nginx/cache keys_zone=one:10m;
      # - /data/nginx/cache -> cache location
      # - keys_zone -> name of shared memory zone
      # - 10m -> size of shared memory zone; doesn't limit the
      #          total amount of cached response data
      # `proxy_cache_path` must be within ttp context level

    server {
        proxy_cache one;
          # - this tells the context to cache reponses using
          #   key_zone=one settings

        location / {
            proxy_pass http://localhost:8000;
        }
    }
}
limits amount of
cached response
data
proxy_cache_path .. max_size ??; 
  # note: this limit can be temporarily exceeded

How Nginx caches data
---------------------

Processes involved:

  1. cache manager
       - activated periodically to check cache state
       - if cached data exceeds `max_size`, cache manager removes the least
         accesed data

  2. cache loader
       - runs only once (after nginx starts)
       - loads metadata about previously cached data into share memory zone
       - loading cache at startup may slow down nginx during first few minutes
       - to avoid that, configure iterative loading by adding the followingin
         `proxy_cache_path`
           a. loader_treshold - iteration duration on milliseconds
                                (default: 200)
           b. loader_files - max items loaded per iteration (default: 100)
           c. loader_sleeps - delay between iteration in milliseconds
                              (default: 50)

The following setting has iteration which lasts 300 ms
or until 200 items have been loaded:

proxy_cache_path /data/nginx/cache keys_zone=one:10m loader_threshold=300 loader_files=200;

Specifying which requests to cache
----------------------------------

- by default, Nginx cache all reponses (received from proxied server) to HTTP
  GET and HEAD requests
- requests and cached responses have key identifiers
- if a request has same key as the cached responsed, Nginx sends the cached
  response to the client
- http, server, and location contexts can be controlled on what responses to
  cache

changes the request characteristics
used in calculating the key
proxy_cache_key "$host$request_uri$cookie_user";
defines the minimum number of times
that a request with the same key
must be made before the response
is cached
proxy_cache_min_uses 5;
cache responses to requests with
methods other than GET and HEAD
proxy_cache_methods GET HEAD POST;


Limiting/Bypassing Caching
--------------------------

- by default, responses remain in cache indefinitely
- they are only removed when:
    a. exceeds max configured size
    b. in order of time since last requested
- cached responses can be set on how long they can be valid by including in any
  of these contexts:
    a. http
    b. server
    c. location
- caching can by bypassed

way of limiting cache validity
proxy_cache_valid 200 302 10m;
  # responses w/ code 300/302 are valid for 10 minutes

proxy_cache_valid 404      1m;
  # responses w/ code 404 are valid for 1 minute

proxy_cache_valid any 5m;
  # any response are valid only for 5 minutes
bypasses caching when specific
condition(s) is/are met
proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment;
  # if atleast 1 parameter is not empty, Nginx sends requests to
  # proxied server
bypasses caching entirely
proxy_no_cache $http_pragma $http_authorization;

Purging Cache
-------------

- outdated cache files can be removed
- necessary to prevent sending old and new versions of web pages at the same
  time
- cache is purged by receiving a special "purge" request

Configuring cache purge
1. On the http level, create a new variable, for example, $purge_method,
   that will depend on the $request_method variable:

http {
...
map $request_method $purge_method {
PURGE 1;
default 0;
}
}

2. In the location where caching is configured, include the proxy_cache_purge
   directive that will specify a condition of a cache purge request. In our
   example, it is the $purge_method configured at the previous step:

server {
listen      80;
server_name www.example.com;

location / {
proxy_pass  https://localhost:8002;
proxy_cache mycache;

proxy_cache_purge $purge_method;
}
}
Sending purge command
# When cache is enabled, you can send purge command by:

$ curl -X PURGE -D – "https://www.example.com/*"
HTTP/1.1 204 No Content
Server: nginx/1.5.7
Date: Sat, 01 Dec 2015 16:33:04 GMT
Connection: keep-alive

# - resources that have a common URL part (specified by the asterisk wildcard) will be removed
# - However, such cache entries will not be removed completely from the cache: they will remain
# - on the disk until they are deleted for either inactivity (the inactive parameter of
# - proxy_cache_path), or processed by the cache purger process, or a client attempts to access them.
Restricting access to
purge command
geo $purge_allowed {
   default         0;  # deny from other
   10.0.0.1        1;  # allow from localhost
   192.168.0.0/24  1;  # allow from 10.0.0.0/24
}

map $request_method $purge_method {
   PURGE   $purge_allowed;
   default 0;
}
Removes files completely
from cache
proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=mycache:10m purger=on;
  # `purger=on` removes cache that matches an asterisk

Byte-Range Caching
------------------

- initial cache fill operation may take some time on large files
- when 1st request starts downloading a part of a video file, next requests will
  have to wait for the entire file to be downloaded and put into cache
- Nginx can cache range requests by using "cache slice module"
- Nginx must be compiled with "slice" module to work

Enables Byte-Range caching
1. Make sure your NGINX is compiled with the slice module.

2. Specify the size of the slice with the slice directive:
location / {
slice  1m;
}

   The slice size should be adjusted reasonably enough to make
   slice download fast. A too small size may result in excessive
   memory usage and a large number of opened file descriptors
   while processing the request, a too large value may result
   in latency.

3. Include the $slice_range variable to the cache key:
proxy_cache_key $uri$is_args$args$slice_range;

4. Enable caching of responses with 206 status code:
proxy_cache_valid 200 206 1h;

5. Enable passing range requests to the proxied server by
   passing the $slice_range variable in the Range header field:
proxy_set_header  Range $slice_range;
Example config
location / {
    slice             1m;
    proxy_cache       cache;
    proxy_cache_key   $uri$is_args$args$slice_range;
    proxy_set_header  Range $slice_range;
    proxy_cache_valid 200 206 1h;
    proxy_pass        http://localhost:8000;
}

Friday, June 22, 2018

Compression/Decompression in Nginx


Introduction
------------

- compression saves bandwidth but has extra cpu overhead
- by default, nginx compresses data before sending to client
- nginx doesn't double compress data (e.g response from a proxied server)
- to enable comression on proxied request, use `gzip_proxied`
- compression directives can be included in any of the ff contexts:
    a. http
    b. server
    c. location

Enabling Compression
--------------------

enables compression for
MIME type text/html
gzip on;

compresses data with
other MIME types
gzip_types text/plain application/xml;
gzip on;
specify minimum length
of response to compress
(default is 20 bytes)
gzip_min_length 1000;
enables compression on
proxied requests
gzip_proxied no-cache no-store private expired auth;
  # - compresses responses whose "Cache-Control" header field has
  #   any of the following:
  #    a. no-cache
  #    b. no-store
  #    c. private
  # - `expired` tells nginx to check the "Expires" field
  # - `auth` tells nginx to check the "Authorization" field
actual example
server {
    gzip on;
    gzip_types      text/plain application/xml;
    gzip_proxied    no-cache no-store private expired auth;
    gzip_min_length 1000;
    ...
}

Enabling Decompression
----------------------

- some clients doens't support responses with `gzip` encoding
- nginx can decompress response on the fly to accomodate such clients
- `gunzip` directive is used for decompression
    -> might not be included in Nginx opensource
    -> can be placed on same contexts as `gzip` directive

enables runtime decompression
location /storage/ {
    gunzip on;
    ...
}

Sending Compressed Files
------------------------

Sends a compressed version
of the file instead of the
regular one
location / {
  gzip_static on;
    # - a requested /file will be sent as /file.gz to client
    # - if file doesn't exist/cient doesn't support gzip,
    #   Nginx sends uncompressed version
    # - `gzip_static` doesn't enable on-the-fly compression;
    #   it simply sends the compressed file
    # - to compress content at runtime, use `gzip` directive
}

Thursday, June 21, 2018

Nginx Virtual Servers


Overview
--------

high level - defines handling of URLs
low level - set of virtual servers for a particular IPs

Virtual servers:

  - similar to VirtualHost in apache
  - contains `location`'s for controlling processing of specific set of URI's

Setting Up Virtual Servers
--------------------------

- nginx.conf requires atleast 1 `server` directive

format
http {
    server {
        # Server configuration
    }
}
w/ `listen` directive
server {
    listen 127.0.0.1:8080;
      # - if port is ommited, standard port is used
      # - if address is ommited, server listens on all addresses
      # - if whole `listen` directive is ommited, standard port
      #   is 80/tcp and default port is 8000/tcp
    ...
}
listening on multiple ports
srver {
  listen 192.168.1.100:8080;
  listen 192.168.1.100:8081;
}
using `server_name`
server {
    listen 80;
      # - If there are several servers that match the IP address
      #   and port of the request, NGINX Plus tests the request’s
      #   Host header field against the server_name directives

    server_name example.org www.example.org;
      # - can be exact name, wild card, or regular expression
      # - regular expressions make use of `~`
      # - if several names match the host header, nginx chooses by"
      #    1. exact name
      #    2. longest wildcard starting with `*` (e.g *.example.org)
      #    3. longest wildcard ending with `* (e.g mail.*)
      #    4. first matching regular expression
      #    5. routes request to the default server; default server is
      #       the 1st listed in nginx.conf or the one with
      #       `listen 80 default_servr` directive
    ...
}

Locations
---------

- nginx can handle requests based on the requested URI
- that is done by using `location` within a `server` block
- nginx first compares URI with prefix string then to a regular expression
- higher priority is given to regex (unless ^~ is used)

Order of search:

1. Test the URI against all prefix strings.
2. The = (equals sign) modifier defines an exact match of the URI and a prefix
   string. If the exact match is found, the search stops. If the ^~
   (caret-tilde) modifier prepends the longest matching prefix string, the
   regular expressions are not checked.
3. Store the longest matching prefix string.
4. Test the URI against regular expressions.
5. Break on the first matching regular expression and use the corresponding
   location.
6. If no regular expression matches, use the location corresponding to the
   stored prefix string.


matches the following:

  /some/path/
  /some/path/document.html


server {
  location /some/path/ {
    # doesn't match /my-site/some/path
      ...
  }
  ...
}
matches URI's with .html
or .html at any position
location ~ \.html? {
    # uses `~` to signify are regular expression
    ...
}
searches stop if encounters
`/`
location = / {
    ...
}
performs different actions
on different URIs
server {
    location /images/ {
        root /data;
          # - serves content under /data
          # - if client requests for /images/file.jpg,
          #   nginx will server /data/images/file.jpg
    }

    location / {
        proxy_pass http://www.example.com;
          # - redirect request to a backend server
    }
}
this regex will accept any
of the ff URIs:

location ~ /data/? {
  autoindex on;
}
order of search demo
  location /dir1/ {  # this will not take effect
    root /data;
  }

  location  ~ /dir1/ {  # this will take effect even it comes later
    root /data2;        # on the list because regex takes precedence
  }

Using Variables
---------------

- calculated at runtime
- preceeded by `$`
- pre-defined variables are available
- custom variable can be defined using: set, map, geo
- e.g: $remote_addr contains client's and $uri holds
  the current URI value

Returning Status Codes
----------------------

- uses `return` directive
- can be used inside `location` and `server` contexts

returns 404 when the wrong
URI was requested by client
location /wrong/url {
    return 404;
}
returns a redirect
  - available for status codes
    301, 302, 303, and 304
location /permanently/moved/url {
    return 301 http://www.example.com/moved/here;
}

Rewriting URIs
--------------

- makes use of `rewrite` directive
- it can be used inside both `server` and `location` directives
- sequence:
   1. nginx process a set of rewrite instructions
   2. selects location context according to new URI
   3. if selected location contains rewrites, those are process in turn
   4. then a new URI s formed and so on..
- 2 parameters that interrupts processing of rewrites:
   1. last - skips the rest of rewrite under the current server/location
             directive; the new URI can still be rewrited by other server
             or location directives
   2. break - stops processing of other rewrites on the current server
              or location directive, cancels search for locations
              matching the new URI, and prevents execution of rewrites on
              the new location

re-writes a specific URI
location /users/ {
    rewrite ^/users/(.*)$ /show?user=$1 break;
      # - 1st parameter: regex matching the URI
      # - 2nd paremeter: substituted URI
      # - 3rd parameter: `break` halts processing of further `rewrite` directive
}
`rewrite` with `return`
directive
server {
    ...
    rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last;
      # `last` flag allows the 2nd `rewrite` below to be
      #  skipped when this 1st on is matched
    rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra  last;
    return  403;
      # if no `rewrite` was matched above, nginx will return 403
    ...
}
replaces `http` with `https`
location / {
    sub_filter     'href="http://127.0.0.1:8080/'    'href="https://$host/';
    sub_filter     'img src="http://127.0.0.1:8080/' 'img src="https://$host/';
    sub_filter_once on;
}

Handling Errors
---------------

- nginx can return a custom page alongside with an error code
- a redirect can also be process
- `error_page` is processed only when errors occured
- `return` is processed immediately even w/o errors

specifies /404/html to return 404
status code
error_page 404 /404.html;
returns a "not allowed"
(status code 405) message
location /private/ {
  return 405;
}
replaces 404 with 301 and redirect
clients to a specific URI
location /old/path.html {
    error_page 404 =301 http:/example.com/new/path.html;
}
a better example
server {
    ...
    location /images/ {
        root /data/www;
          # tells where to search for the file

        open_file_cache_errors off;
          # prevents writing errors when file is not found

        error_page 404 = /fetch$uri;
          # make an internal redirect if the file is not found
    }
    location /fetch/ {
        proxy_pass http://backend/;
          # if /images/pic.jpg is not found, here it will be
          # process as http://backend/fetch/images/pic.jpg
    }
}