2. proxy fontend 與 backend

最後更新: 2019-04-09

目錄

 


Simple Load Balancing

 

Diagram

                   /--> apache1 (1.11)
(wan ip)nginx(1.1) ---> apache2 (1.12)
                   \--> apache3 (1.13)    

Simple Load Balancing

http {

  # 定義一個叫 myproject 既 Backend Group
  upstream myproject {

    # Load Balancing Method
    # Default 係用 "weighted round-robin" 去分配 request 到 backend 的
    # least_conn;       # 連去 connection 最小的 backend ( 一般來說它負載最小 )
    ip_hash;            # The key for the hash is the class-C network address (佢有 sticky 的效果)

    # server address [parameters];
    # 相當於 "server n.n.n.n:80 weight=1 max_fails=1 fail_timeout=10;"
    # 意思是 10 秒內有 1 次沒反應就當 Server 死了, 其後 10 秒不 forward request 到去
    # max_fails=0 <= disables the accounting of attempts
    server 192.168.1.11:80;

    # It will be passed requests when the primary servers are down.
    server 192.168.1.12:80 backup;

    # max_conns: limits the maximum number of simultaneous active connections to the proxied server (1.11.5)
    # If the server group does not reside in the shared memory, the limitation works per each worker process
    # Shared memory syntax: zone name [size];
    # Default: 0 (no limit)
    server 192.168.1.13:80 max_conns=50;

    # marks server as permanently offline
    server 192.168.1.14:80 down;

    # commercial subscription only
    # 判斷 Server 的 healthy
    # Module: ngx_http_upstream_module
    health_check interval=3 fails=3 passes=3;

    keepalive 32;
  }
 
  # 定義一個 proxy
  server {
    listen 80;
    server_name domain.com www.domain.com;
    location / {
      include     /etc/nginx/proxy_params;
      proxy_pass  http://myproject;
      proxy_bind  192.168.1.1;                      # 設定 outgoing 去 upstream 的 interface/address 
    }

  }
}

health_check

# default, 5 seconds;
# fails: 連續多少次連不到 uri 就認為 Server unhealthy,  default, 1;
# passes: 連續多少次連到才認為 healthy
# uri: default, "/"
# match:  by default, the response should have status code 2xx or 3xx.

health_check [interval=time] [fails=number] [passes=number] [uri=uri] [match=name];

http {
    server {
    ...
        location / {
            proxy_pass http://backend;
            health_check match=welcome;
        }
    }

    match welcome {
        status 200;
        header Content-Type = text/html;
        body ~ "Welcome to nginx!";
    }
}

keepalive

# maximum number of idle keepalive connections to upstream servers that are preserved in the cache

# When this number is exceeded, the least recently used connections are closed.

# keepalive directive does not limit the total number of connections to upstream servers

keepalive 96;

P.S.

keepalive_timeout 60s;           # Default: 60
keepalive_requests 100;         Default: 100

gzip to backend

nginx is not using gzip to talk to backend servers

Nginx is a HTTP/1.0 reverse proxy, gzip compression was not in the HTTP specification until HTTP/1.1.

/etc/nginx/proxy_params 的內容

proxy_set_header Host              $host;
proxy_set_header X-Real-IP         $remote_addr;
proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

P.S.

# 在 Nginx 後面的 Apache 會獲得 HTTP_X_FORWARDED_PROTO

$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.

應用:

能用在 .htaccess

RewriteEngine on
# Rewrite to SSL for anything coming off of a proxy.
RewriteCond %{HTTP:X-Forwarded-Proto} ^http$
RewriteRule ^(.*) https://%{SERVER_NAME}/$1 [L,R=301]

%{HTTP:header} 係一個 mod_rewrite 的 variable 來

php code:

$_SERVER["HTTPS"] returns "on" when it's https:// 在 https offload proxy 時用不到.

$_SERVER['HTTP_X_FORWARDED_PROTO'];

 


Reverse Proxy with Caching

 

Setting

http {

    # 建立用來存放 Cache 的 Folder
    # mkdir -p /var/cache/nginx/tmp /var/cache/nginx/cache
    # chown nginx. /var/cache/nginx -R
    # chmod 770 /var/cache/nginx -R

    # A directory for storing temporary files with data received from proxied servers
    proxy_temp_path   /var/cache/nginx/tmp    1 2

    # level: 設定 subdirectory 的層數, 格式是 1, 1:1, 1:2:3
    # keys_zone: 建立一個名叫 STATIC 的 cache, 並且有 64 Mbyte Ram
    # inactive: 多久沒有人 access 就 Delete 了 cache 了的檔案
    # max_size: 那 cache folder 的 Total size
    # proxy_cache_path and proxy_temp_path 一定要在同一個 filesystem
    proxy_cache_path  /var/cache/nginx/cache  levels=1:2    keys_zone=MyCache:10m
                                              inactive=24h  max_size=1g;

    server {
        location / {

            include                /etc/nginx/proxy_params;
            proxy_pass             http://BACKEND_IP;

            # proxy_cache zone | off (default);
            # off => disables caching
            proxy_cache            MyCache;

            # 在 response 加入了 X-Cache-Status: MISS | HIT |  | BYPASS (中左 proxy_cache_bypass)
            add_header X-Cache-Status $upstream_cache_status;

            proxy_cache_valid      200  1d;

            proxy_cache_use_stale  error timeout invalid_header updating
                                   http_500 http_502 http_503 http_504;
        }
    }
}

proxy_cache_valid: (這設定是必須的, 否則 nginx 就不會 cache 東西)

Usage: proxy_cache_valid [code ...] time;

# Default 只有 200, 301 and 302 才會 cache

proxy_cache_valid    1d;

Unit: m, h, d

# 設定不同 return code 有不同的 cache time

proxy_cache_valid    404  1m;

# 其餘 code 用 any

proxy_cache_valid any      1m;

* Upstream cache-related directives have priority over proxy_cache_valid value

proxy_ignore_headers: Disables processing of certain response header fields from the proxied server.

Override Upstream:

proxy_ignore_headers X-Accel-Expires Expires Cache-Control;

proxied server 的不同 cache header 的 priority:

  1. X-Accel-Expires
    n    => seconds
    0    => disables caching for a response
    @X => absolute time(seconds since Epoch)
  2. Expires/Cache-Control
  3. If the header includes the “Set-Cookie” field, such a response will not be cached.
  4. If the header includes the “Vary” field with the special value “*”, such a response will not be cached
  5. proxy_cache_valid

P.S.

nginx 不會 cache 有 Set-Cookie header 的東西 !

亦不會 cache Cache-Control 的值是 "no-cache", "no-store", "private",  "max-age=0"

proxy_cache_use_stale:

# 當什麼時候照用 stale cache

# Default: off
# 還有設定 http_404
proxy_cache_use_stale     error timeout invalid_header updating
                          http_500 http_502 http_503 http_504;

proxy_cache_key

Default:

proxy_cache_key    $scheme$proxy_host$request_uri;

$proxy_host

The name and port of the upstream server that handled the request.

proxy_cache_lock

# only one request at a time will be allowed to populate a new cache element passing to a proxied server
# identified according to the proxy_cache_key directive
# Other requests of the same cache element will either
# wait for a response to appear in the cache or
# the cache lock for this element to be released (proxy_cache_lock_timeout=5s;)

proxy_cache_lock on;

Cache Control

什麼情況不 cache

  • proxy_cache_bypass string
                 string not empty and is not equal to “0” then the response will not be taken from the cache
  • proxy_no_cache
                 which the response will not be saved to a cache
proxy_cache_bypass  $http_cache_control;
proxy_cache_bypass  $http_secret_header;
proxy_no_cache      $http_cache_control;

proxy_ignore_headers

proxy_ignore_headers Set-Cookie;
proxy_ignore_headers Cache-Control;

proxy_ignore_headers

Disables processing of certain response header fields from the proxied server

設定某目錄在 browser 的 cache ttl

location / {
    # ...
    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires 1y;
        log_not_found off;
    }
}

設定某類 file 在 browser 的 cache ttl

if ($request_uri ~ \.css$) {
    expires max;
}

Bownser 的 refresh 情況

Request headers:

按 Enter                  -> 沒有 "Cache-Control"

按F5                       -> Cache-Control:max-age=0

按 Ctrl + F5            -> Cache-Control:no-cache

針對 Request headers 的 cache control

HTTP_CACHE_CONTROL is the value of the Cache-Control header received from the client.

It's not something configured on your server, it's based on what the client sends.

# 當 Request headers 內的 Cache-Control 是 no-cache 時,

# 就會把 $clearcache 設定成 1

Context: http

map $http_cache_control $clearcache {
    default          "";
    "no-cache"       1;
}

# Cache setting

Context: http, server, location

proxy_cache_bypass $clearcache;

不安全的做法 (直食 Request headers)

# $http_X 是 http header 來

proxy_cache_bypass $http_bypass_cache;

# $http_bypass_cache 相當於 bypass-cache

curl http://mydomain/mypage.html -s -I -H "bypass-cache"         # 無效

curl http://mydomain/mypage.html -s -I -H "bypass-cache:1"      # 有效

curl http://mydomain/mypage.html -s -I -H "bypass-cache:true"  # 有效

Browser

RFC 2616

"00:00:00 UTC, January 1, 1970"

If a "Cache-control: max-age=N" header is specified, then the freshness lifetime is equal to N.

strong validator.
If the "ETag" header is present in a response, then the client can issue an "If-None-Match" request header to validate the cached document.

weak validator
"Last-Modified" response header

# forces the browser to request a fresh version of them every time
# Therefore they are not retrieved from the browsers cache.

But don't you end up serving stale content?
Nope, each document stored in the mozilla cache is given an expiration time.  If mozilla tries to load the document for normal viewing, this expiration time is honored.  A cached document will be validated with the server if necessary before being shown to the user.

Firefox view cache

about:cache

只 Cache 部份 File

include     /etc/nginx/proxy_params;
location ~*
    \.(bmp|jpg|jpeg|png|svg|ico|gif|mp4|css|js|html|txt|zip|rar)$ {
    add_header      X-Cache-Status $upstream_cache_status;
    proxy_pass      http://MyBackend;
    proxy_bind      192.168.123.14;
    log_not_found   off;
    access_log      off;
    proxy_cache     cache-www;
    proxy_buffering on;
    proxy_cache_valid 200 30d;
    proxy_cache_valid 301 302 10m;
    proxy_cache_valid 404 1m;
    proxy_cache_min_uses 3;
    proxy_cache_key $host$uri$is_args$args;
    proxy_cache_use_stale error timeout updating;
    proxy_cache_lock on;
    #proxy_cache_purge $purge_method;
}
location / {
    ...
}

 


再談 proxy_cache_key

 

以下兩 link 不是同一份 Cache 的

Setting

set $cache_key             $host$uri$is_args$args;
proxy_cache_key            $cache_key;
add_header X-Cache-Status  $upstream_cache_status;
add_header X-CACHE-KEY     $cache_key;

Test Code

<?
  print_r($_GET);
?>

Test

curl -I 'https://example.com/?a=1&b=2'

curl -I 'https://example.com/?b=2&a=1'

令它們同一份 Cache 的方案

[1] using normalized URI

set $cache_key $host$uri${is_args}a=${arg_a}&b=$arg_b;

[2] use map

a)

map $args $my_args {
        default $args;
        ~^a=(.*)&b=(.*)$    a=${1}b=${2};
        ~^b=(.*)&a=(.*)$    a=${2}b=${1};
}

b)

set $cache_key $host$uri${is_args}$my_args;

[2] use if

if ($args ~ ... {
    set $args ...;
}
if ($args ~ ... {
    set $args ...;
}

Remark

$is_args is an empty string if there are no arguments, or a ? to signify the start of the query string.

$args then adds the arguments ($query_string could also be used with the same result).

$request_uri 近似于 $uri$is_args$args

$request_uri : full original request URI (with arguments)

$request_uri vs $uri

$uri

The $uri variable is set to the URI that nginx is currently processing,

but it is also subject to normalisation, including:

  • Removal of the ? and query string
  • Consecutive / characters are replace by a single /
  • URL encoded characters are decoded

$request_uri

The value of $request_uri is always the original URI and is not subject to any of the above normalisations.

Most of the time you would use $uri, because it is normalised.

Using $request_uri in the wrong place can cause URL encoded characters to become doubly encoded.

 


proxy_redirect

 

Replaceable  "Location" and "Refresh" in proxied server response-header

Syntax:

  • proxy_redirect default;   # Default
  • proxy_redirect off;
  • proxy_redirect redirect replacement;

設定:

假設有設定

proxy_redirect http://localhost:8000/two/ http://frontend/one/;

Remark: 可以縮寫成

proxy_redirect http://localhost:8000/two/ /;

Suppose a proxied server returned the header field

Location: http://localhost:8000/two/some/uri/

Will rewrite this string to

Location: http://frontend/one/some/uri/

proxy_redirect default

location /one/ {
    proxy_pass     http://upstream:port/two/;
    proxy_redirect default;

相當於

location /one/ {
    proxy_pass     http://upstream:port/two/;
    proxy_redirect http://upstream:port/two/ /one/;

e.g.

[1]

# port & servername re-write
proxy_redirect http://localhost:8000/two/ http://frontend/one/;
# SSL Offload for OWA
proxy_redirect http://MyDomain/owa https://MyDomain:8443/owa;

[2]

# The off parameter cancels the effect of all proxy_redirect directives on the current level:
# There could be several proxy_redirect directives:

proxy_redirect off;
proxy_redirect default;
proxy_redirect http://localhost:8000/  /;
proxy_redirect http://www.example.com/ /;

[3]

A redirect can also contain (1.1.11) variables:

proxy_redirect http://$proxy_host:8000/ /;

 


Nginx 與 Apache 的 proxy 對應設定

 

apache:

<VirtualHost myhost:80>
    ServerName myhost
    DocumentRoot /path/to/myapp/public

    ProxyPass / http://myapp:8080/
    ProxyPassReverse / http://myapp:8080/
</VirtualHost>

ngnix:

server {
  listen myhost:80;
  server_name  myhost;
  location / {
    root /path/to/myapp/public;

    proxy_pass http://myapp:8080;

    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
  ...
}

** 沒有 server_name 時, nginx  是不會開始 listen port 的 !!

 


proxy_set_header

 

Syntax:

proxy_set_header field value;

The value can contain text, variables, and their combinations.

These directives are inherited from the previous level

    if and only if there are no proxy_set_header directives defined on the current level.

By default:

proxy_set_header Host       $proxy_host;
proxy_set_header Connection close;

X-Forwarded-X (de-facto standard header)

X-Forwarded-For (XFF)

for identifying the originating IP address of a client connecting to a web server through an HTTP proxy

X-Forwarded-Host (XFH)

for identifying the original host requested by the client in the Host HTTP request header.

X-Forwarded-Proto (XFP)

protocol (HTTP or HTTPS) that a client used to connect to your proxy

X-Forwarded-Port

for identifying the port that the client used to connect to the load balancer

$proxy_X

$proxy_add_x_forwarded_for

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.

$proxy_host

name and port of a proxied server as specified in the proxy_pass directive;

$proxy_port

port of a proxied server as specified in the proxy_pass directive

MySetting

proxy_set_header   Host $host:$server_port;
proxy_set_header   X-Forwarded-Host $host:$server_port;

 


進階的 proxy 設定

 

server{
  ...
  access_log  off;
  error_log   off;

  # 不用 log 常見的 file
  location = /favicon.ico { access_log off; log_not_found off; }
  location = /robots.txt { access_log off; log_not_found off; }

  # 直接在 proxy 上 deny 了
  location ~ /\. { deny all;  }

  #location ~ /\.ht {
  #  deny          all;
  #  access_log    off; 
  #  # files not found on disk
  #  log_not_found off;
  #}

  #################################################### local error page
  error_page 500 502 503 504  /50x.html;
  error_page 404              /404.html

  location = /404.html {
      root   /usr/share/nginx/html;
  }
  location = /50x.html {
      root   /usr/share/nginx/html;
  }

  #################################################### 主要 setting
  location / {
        include     /etc/nginx/proxy_params;
        proxy_pass  http://MyBackend;
  }

  location ~*  \.(bmp|jpg|jpeg|png|swf|ico|gif|mp4|css|js|html|txt|zip|rar|gz)$ {
        include            /etc/nginx/proxy_params;
        proxy_pass         http://MyBackend;

        # buffer size for reading client request body (POST)
        # Default: 32b=>8K, 64bit=>16K
        # over => written to a temporary file (client_body_temp_path)
        client_body_buffer_size    128k;

        # mkdir /var/lib/nginx/client_temp -p
        # chown www-data. /var/lib/nginx/client_temp
        # chmod 770 /var/lib/nginx/client_temp
        # 必須設定它, 否則當 POST over 了 client_body_buffer_size 時就會失敗
        # (過 proxy_read_timeout 就會出 504)
        client_body_temp_path /var/lib/nginx/client_temp 1 2;

        # request header field:“Content-Length”
        # exceeds => 413 (Request Entity Too Large) error
        # 0  => disables checking of client request body size.
        client_max_body_size       10m;

        # 初始為 connect 的 timeout (Client 第一次 connect 上來)
        # Default: 60s
        proxy_connect_timeout      10;

        # Between two write successive operations(not on entire transfer of request)
        # timeout with the transfer of request to the upstream server
        proxy_send_timeout         10;
        # read timeout for the response of the proxied server
        proxy_read_timeout         10;

        # 當 buffer 是 off 時, cacherate limiting 都會唔 work !! (Default: on)
        # If the whole response does not fit into memory(proxy_buffer_size),
        # a part of it can be saved to a temporary file on the disk(proxy_max_temp_file_size)
        proxy_buffering            on;

        # read the first part of the response, obtained from the proxied server
        # By default, the buffer size is equal to one memory page(4k | 8k)
        # usually contains a small response header
        proxy_buffer_size          4k;

        # for reading a response from the proxied server (for a single connection)
        # number and the size of buffers(read the answer obtained from the proxied server)
        proxy_buffers              4 32k;
        
        # If file is larger than this size, 
        # it will be served synchronously from upstream server rather than buffered to disk.
        # 0: temporary files usage will be disabled
        proxy_max_temp_file_size   1m;

        # flushed to the proxy_temp_path when writing
        proxy_temp_file_write_size 128k;

        # Enabled: the entire request body is read from the client before sending the request to a proxied server.
        # Disabled: the request body is sent to the proxied server immediately as it is received.
        # When HTTP/1.1 chunked transfer encoding is used to send the original request body,
        # the request body will be buffered regardless of the directive value unless HTTP/1.1 is enabled for proxying.
        proxy_request_buffering on
  }

  location /software/ {
    proxy_pass       http://192.168.3.106:8080;
    proxy_redirect   http://192.168.3.106:8080   http://datahunter.org/software/;
  }
}

proxy_no_cache

Specifies in what cases a response will not be cached

e.g.

# the response will never be cached if the cookie "nocache" is set in the request.

proxy_no_cache  $cookie_nocache  $arg_nocache  $arg_comment;

proxy_cache_bypass

Answer will not be taken from the cache.

當 string variable 不是 empty0 時, 就不用 cache

e.g.

假設 client request 的 header 有 "My-Secret-Header: 1" 時, 那就會 bypass

proxy_cache_bypass $http_my_secret_header;

proxy_cache_lock

# Default: off

proxy_cache_lock                on;
proxy_cache_lock_timeout   5;

proxy_cache_min_uses

# 某 file 被 client 存取幾多次後才開始被 cache. Default 1 <-- 亦即是不用改

proxy_cache_min_uses        3;

# Catch the wordpress cookies.

# Must be set to blank first for when they don't exist.
set $wordpress_auth "";
if ($http_cookie ~* "wordpress_logged_in_[^=]*=([^%]+)%7C") {
    set $wordpress_auth wordpress_logged_in_$1;
}

# 2 rules to dedicate the no caching rule for logged in users.

proxy_cache_bypass $wordpress_auth; # Do not cache the response.
proxy_no_cache     $wordpress_auth; # Do not serve response from cache.

 


Stale Cache

 

作用

解決 "Cache stampede" 問題

reduce requests that has to come through upstream(Backend)

Usage

This serves out of date elements from cache, and issuing updates from backend.

So when your backend servers a page after 2 seconds, the client already got the old ressource from cache.

The second locks one element from cache, while already a update-cache request to backend is running,

so there are no paralell requests for one cache-item.

proxy_cache_lock               on;
proxy_cache_use_stale          error timeout updating;
proxy_cache_background_update  on;

proxy_cache_lock

Default: off

only one request at a time will be allowed to populate a new cache element identified according to the proxy_cache_key directive by passing a request to a proxied server.

up to the time set by the "proxy_cache_lock_timeout 5s;" (Default)

proxy_cache_use_stale

Default: off

Determines in which cases a stale cached response can be used

Syntax:

proxy_cache_use_stale error | timeout | invalid_header | updating |
                      http_500 | http_502 | http_503 | http_504 |
                      http_403 | http_404 | off (Default);

error: using a stale cached response if a proxied server to process a request cannot be selected

updating: permits using a stale cached response if it is currently being updated

proxy_cache_background_update

Default 都是 off

Allows starting a background subrequest to update an expired cache item,

while a stale cached response is returned to the client.

 


Backend Server Setting (Apache)

 

.htaccess

<Files ~ "\.(htaccess|htpasswd|ini|bak|old|log|sh|sql)$">
    deny from all
</Files>

RewriteEngine On
# all to www
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^(.*)$ http://www.%{HTTP_HOST}/$1 [R=301,L]
# single enter point
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !\.(jpg|png|css|gif|js|ico)$
RewriteRule ^(.*)$ index.php [L,QSA]

log setting

<IfModule log_config_module>

    # The following directives define some format nicknames for use with a CustomLog directive
    #LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    #LogFormat "%h %l %u %t \"%r\" %>s %b" common
    LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%a %l %u %t \"%r\" %>s %b" common
    <IfModule logio_module>
      # You need to enable mod_logio.c to use %I and %O
      LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
    </IfModule>

    ....

</IfModule>

 * 將 %h 改成 %a

%h     Remote hostname. Will log the IP address if HostnameLookups is set to Off (Default: Off)

%a     Client IP address of the request (see the mod_remoteip module)

More info.

https://datahunter.org/apache24_module#mod_remoteip

 


SSL-Offloader

 

nginx.conf

server {

    listen             443;  
    server_name        datahunter.org;  
 
    # SSL Setting
    ssl                on;

    #Default:          off
    ssi                on;

    # Default:         off
    ssi_silent_errors  on;

    ssl_certificate        /etc/nginx/ssl/server.pem;  
    ssl_certificate_key    /etc/nginx/ssl/server.key;  

    ssl_protocols          TLSv1.2;

    ssl_session_timeout    30m;

    ssl_session_cache shared:SSL:10m;

    location / {  
        include     /etc/nginx/proxy_params;
        proxy_pass  http://MyBackend;
 
        proxy_cache_min_uses 3;
        proxy_cache_key $host$uri$is_args$args;
 
        # re-write redirects to http as to https, example: /home  
        proxy_redirect http:// https://;  
    }
        location = /favicon.ico { access_log off; log_not_found off; }
        location = /robots.txt { access_log off; log_not_found off; }
        location ~ /\. { deny all;  }

        access_log              /var/log/nginx/ssl.log;
        access_log              /var/log/nginx/ssl-cache.log cache_status;
}

Doc: http://wiki.nginx.org/SSL-Offloader

 


proxy_store

 

This directive can be used to create local copies of static unchangeable files

proxy_store

# Enables saving of files to a disk. Default: off
# on:      saves files with paths corresponding to the directives alias or root
# string:  the file name can be set explicitly using the string with variables

Syntax: proxy_store on | off | string;

i.e.

proxy_store /data/www$original_uri;

* The response is first written to a temporary file(proxy_temp_path), and then the file is renamed( 移去 proxy_store 位置 ).

proxy_store_access

# Sets access permissions for newly created files and directories

proxy_store_access user:rw group:rw all:r;

Usage Example

[1]

location /images/ {
    root               /data/www;
    error_page         404 = @fetch;
}

location @fetch {
    internal;

    proxy_pass         http://backend;

    proxy_store        on;
    proxy_store_access user:rw group:rw all:r;

    # 一定要用同一個 FileSystem
    proxy_temp_path    /data/temp;
    root               /data/www;
}

[2]

location ~ .*\.(js|css|jpg|png|mp4|doc|docx|ppt)$ {
        expires            120h;
        proxy_store        on;
        proxy_store_access user:rw group:rw all:rw;
        root               /data/cache/a;        
        proxy_temp_path    /data/cache/b;
        include            /etc/nginx/proxy_params;
        if ( !-e $request_filename) {
                proxy_pass  http://127.0.0.1:8080;
        }
}

"@"

The “@” prefix defines a named location.

Such a location is not used for a regular request processing, but instead used for request redirection.

They cannot be nested, and cannot contain nested locations.

 


Maintenance Page

 

假設 index.html 係 maintenance page 的內容

Setting - 1

# maintenance page
server {
    listen          80;

    # 不 log
    access_log      off;
    error_log       /dev/null;

    root            /home/vhosts/maintenance/web;

    location / {
        # 留 cache
        expires   60s;

        index     index.html;

        # 找不到的 URL 都去/index.html
        try_files $uri $uri/ /index.html;
    }
}

Setting - 2

server {

        ...
        
        location / {
                # ACL
                deny 1.1.1.1;
                deny 123.123.123.123;

                # maintenance
                try_files /maintenance.htm @proxy;

                # Limit req
                limit_req zone=qps2 burst=10 nodelay;
        }
        location @proxy {
                # Proxy Setting
                include     /etc/nginx/proxy_params;
                proxy_pass  http://MyBackend;
        }
}

 


Route by IP

 

簡單版(if)

server {
    # Route by IP
    if ( $remote_addr ~* ip-address-here ) {
        proxy_pass http://Anoter_Backend;
    }
    listen 80;
    proxy_pass http://Default_Backend;
}

進階版(map)

# Backend setting
upstream staging1 {
    server 10.0.0.11:80;
}
upstream staging2 {
    server 10.0.0.12:80;
}

# Route by IP
map $remote_addr $upstream {
    4.5.6.7     staging2;
    default     staging1;
}

# Website
server {
    listen 80;
    proxy_pass http://$upstream;
}

 


proxy_store

 

直接 cache 文件到 local 同樣的目錄, 像 mirror 一樣.
(proxy_store會按照實際請求的url地址建立相應的目錄結構)
但無法通過程序控制 "cache" 何時過期, 需要定期刪除緩存目錄中的內容

 * The modification time of files is set according to the received “Last-Modified” response header field.
 * proxy_temp_path -> root | alias

準備

mkdir -p /home/vhosts/datahunter.org
cd /home/vhosts/datahunter.org/
mkdir web temp
chown www-data. web temp
chmod 750 web temp

設定

upstream MyBackend{
    server 192.168.123.11:80;
}

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|html|htm|css)$ {

    expires                   7d;

    proxy_store on;
    proxy_store_access user:rw group:r ;    

    root            /home/vhosts/datahunter.org/web;
    proxy_temp_path /home/vhosts/datahunter.org/temp;

    log_not_found off;
    access_log off;

    include          /etc/nginx/proxy_params;
    proxy_set_header Accept-Encoding '';

    if ( !-e $request_filename)  {
        proxy_pass http://MyBackend;
    }
}

Remark

如果沒有 "proxy_set_header Accept-Encoding '';" 如果 MyBackend 傳回的資料是 gzip 了,

那 local 會把 gzip 的內容保存為原來名稱

i.e.

file css_c06a06487539ab1845387427cebdb4e0.css

css_c06a06487539ab1845387427cebdb4e0.css: gzip compressed data, from Unix

 


proxy_cache 與 proxy_store 比較

 

proxy_cache 比 proxy_store 有較高效能

1. 以 gzip 保留了 file, 不用二次壓縮

file 5677d21b6492fb95723795f3bd4a69bd

5677d21b6492fb95723795f3bd4a69bd: data

2. 保留了 Backend 回應的資訊

ETag, Last-Modified, Content-Length, Content-Type

 


Custom Log Format

 

Setting

http {

  # log
  log_format cache_status '[$time_local] $remote_addr - $upstream_cache_status '
                          '"$request" $status $body_bytes_sent '
                          '"$http_referer" "$http_user_agent"';
  access_log   /var/log/nginx/access.log cache_status;

  ...
}

Default: access_log logs/access.log combined;

Analysing cache efficiency

awk '{print $5}' /var/log/nginx/access.log | sort | uniq -c | sort -r

     33 -
     24 MISS
    185 HIT

 


client_body_in_?

 

client_body_in_file_only

save the entire client request body into a file (for debugging)

  • off (Default)
  • on: temporary files are not removed after request processing
  • clean: will cause the temporary files left after request processing to be removed

i.e.

client_body_in_file_only on;

client_body_in_single_buffer

client_body_in_file_only 的相反

Default: off

 


proxy_cache_purge

 

 * This functionality is available as part of our commercial subscription.

Usage:

Defines conditions under which the request will be considered a cache purge request.

not empty and is not equal to  0 then the cache entry with a corresponding cache key is removed.

proxy_cache_purge string

If the cache key of a purge request ends with an  "*" , all cache entries matching the wildcard key will be removed from the cache.

entries will remain on the disk until they are deleted for either inactivity, or processed by the cache purger

proxy_cache_key

Defines a key for caching

Default: proxy_cache_key $scheme$proxy_host$request_uri;

$proxy_host

name and port of a proxied server as specified in the proxy_pass directive;

Example

http {
    ...
    geo $purge_allowed {
        default            0;  # deny from other
        127.0.0.1          1;  # allow from localhost
        192.168.0.0/24     1;  # allow from localnet
    }
    map $request_method $purge_method {
       PURGE   $purge_allowed;
       default 0;
    }
}

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

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

        proxy_cache_purge $purge_method;
    }
}

curl send PURGE Command

curl -X PURGE -D – "https://www.example.com/*"

purger

purger=on|off(Default)

Instructs whether cache entries that match a "wildcard key" will be removed from the disk by the cache purger (1.7.12).

on: activate the "cache purger" process that permanently iterates through all cache entries and

      deletes the entries that match the wildcard key. 

purger_files=number # Default: 10

Sets the number of items that will be scanned during one iteration

purger_threshold=number (Default: 50ms)

Sets the duration of one iteration

Example

proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=mycache:10m purger=on;

proxy_cache_path

# V1.7.10
# The directory for temporary files is set based on the "use_temp_path" parameter
# off, temporary files will be put directly in the cache directory.
# on, use the directory set by the proxy_temp_path directive

proxy_cache_path ... use_temp_path=no

proxy_cache_revalidate

proxy_cache_revalidate yes;

Enables revalidation of expired cache items using conditional requests with

the “If-Modified-Since” and “If-None-Match” header fields.

 


Doc

 

http://wiki.nginx.org/HttpProxyModule