動機
之前有用過nginx做reverse proxy連到signal server 提供HTTPS連線
現在想把nginx拿來做別的事,所以先survey一下nginx的基本功能吧
location
exact match
= <pattern>
要剛好一樣,遇到後就停止搜尋,優先序最高
longest prefix match
^~ <pattern>
只要前綴符合就結束其他的比對,優先序第二高
regex match
~ <regex>
or ~* <regex>
可以用?<name>
來設定變數 (Perl 5.10 compatible syntax, supported since PCRE-7.0)
同時這個設定變數(name capture)也可以用在server_name
優先序第三高
prefix match
一般常見的那種,就算把這種match寫在第一個,也會先看之後有沒有前面三種的可以match 優先序最低,如果有regex的match就會用regex的match
named location
類似function
location / {
try_files $uri $uri/ @custom
}
location @custom {
# ...do something
}
proxy
X-Forwarded-For & Forwarded header
X開頭的header都是自訂的header,現在有標準化就可以用標準化的header
Before
X-Forwarded-For: 12.34.56.78, 23.45.67.89
X-Real-IP: 12.34.56.78
X-Forwarded-Host: example.com
X-Forwarded-Proto: https
After
Forwarded: for=12.34.56.78;host=example.com;proto=https, for=23.45.67.89
用頓號區分ip
在官網上有把$remote_addr
(srcIP)與$http_forwarded
(srcIP,port)轉成Forwarded
header的code
map $remote_addr $proxy_forwarded_elem {
# IPv4 addresses can be sent as-is
~^[0-9.]+$ "for=$remote_addr";
# IPv6 addresses need to be bracketed and quoted
~^[0-9A-Fa-f:.]+$ "for=\"[$remote_addr]\"";
# Unix domain socket names cannot be represented in RFC 7239 syntax
default "for=unknown";
}
map $http_forwarded $proxy_add_forwarded {
# If the incoming Forwarded header is syntactically valid, append to it
"~^(,[ \\t]*)*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*([ \\t]*,([ \\t]*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\\t \\x21\\x23-\\x5B\\x5D-\\x7E\\x80-\\xFF]|\\\\[\\t \\x21-\\x7E\\x80-\\xFF])*\"))?)*)?)*$" "$http_forwarded, $proxy_forwarded_elem";
# Otherwise, replace it
default "$proxy_forwarded_elem";
}
proxy_set_header Forwarded $proxy_add_forwarded;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Coexistence with X-
但要注意ticket #1316的問題,$http_forwarded
只會有一個for!!
simple example
server {
listen myhost:80;
server_name myhost;
location / {
root /path/to/myapp/public;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://myapp:8080;
}
}
redirect: rewrite, if, return, set
set
set $<name> val
return & break
return status_code [text | URL]
或是
return URL
break就是break
兩個都是讓rewrite或是if停下來,但return會丟status code與URL
rewrite
rewrite path1 path2 [opt]
path1的比對值是$URI
也就是像hi.php?a=b
path1的pattern只會與hi.php比對
如果需要$query_string
被改寫
要額外處理像
if ($query_string ~* "name=(.*)") {
set $name $1;
rewrite /info.php /result.php?newname=$name break;
## rewrite /info.php /result.php?newname=$1 break;
}
rewrite /info.php /result.php?newname=$1 break;
的坑是
$1
是看path1比對的結果
break, last, redirect , perminate, 空白
- break: 不看其他的rewrite
- last: 回到location再跑一次
- redirect: 302, 臨時的redirect
- perminate: 301, 永久的轉移
- 空白: 繼續往下跑其他rewrite(好像switch阿)
last & break
借一下這裡的例子來分析
rewrite /test2 /tt break;
location /test { ## 1
rewrite /test2 /test3 break;
rewrite /test /test2 last; ## 2
rewrite /test2 /test3 break;
}
location /test2 { ## 3
return 508;
}
location /test3 {
return 503;
}
## /test => 508
現在先把rewrite分成 server -> locations -> location
所以在location的last,會回到上一層在跑一次比對
rewrite /tt /index.html break;
rewrite /test2 /tt last; ## 1
location /test {
rewrite /test2 /test3 break;
rewrite /test /test2 last;
rewrite /test2 /test3 break;
}
location /test2 {
return 508;
}
location /test3 {
return 503;
}
location / {
root html;
index index.html index.htm;
}
## 2 , no match
## test2 => not exist
如果把last看成回到上一層再跑一次, server的last因為沒有上一層可以回去,所以她的行為與break,就是直接到下一層的比對
if
就是bash的if,但是沒有else的部分 可以用location的比對像regex等等
safe if
根據這裡的分析 if會產生自己的location block,如果其中有自己的content handler,就會用,不然就繼承外面的
整個rewrite module,會把他的directive翻成自己的指令 先跑自己所有的指令後才跑其他的directive
像
location /if-try-files {
try_files /file @fallback; ## wont work
set $true 1;
if ($true) { ## here is new location!! forget previous location
# nothing
}
}
所以安全使用if的原則是
- if中盡量只放rewrite module的directive
- 把所有if放在非rewrite module的directive前面
- 或是用其他directive取代if,像
if(-f ...)
換成try_files
Virtual Host
想像有很多nginx.conf(對應一台主機) 都放在sites-available
而實際對外看到的就是在sites-enabled中有symbol link的conf
XSendfile (X-Accel)
就是當網站要送檔案時可以直接用header的內容來告訴nginx去送某個檔案。 而不是把檔案放在body中再丟出去
## /protected/iso.img
location /protected/ {
internal;
root /some/path; ## path: /some/path/protected/iso.img
## alias /some/path/; ## path: /some/path/iso.img
}
basic server conf
server {
listen 8080;
root /data/up1; #從哪邊開始找檔案
location / {
fastcgi_pass localhost:9000; # fastcgi的server在哪?
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # 與bash串接string的方式一樣
fastcgi_param QUERY_STRING $query_string;
}
location ~ \.(gif|jpg|png)$ {
root /data/images;
}
}
full example
user www www; ## Default: nobody
worker_processes 5; ## Default: 1, 能開幾個process
error_log logs/error.log;
pid logs/nginx.pid;
worker_rlimit_nofile 8192; ## 一個process最多能開幾個檔案
events { ## 處理與接收連線有關的參數,實際連線(socket)的參數要去http調
worker_connections 4096; ## Default: 1024 一個process最多能accept幾條連線
}
http {
include conf/mime.types; ## include就是macro展開
include /etc/nginx/proxy.conf;
include /etc/nginx/fastcgi.conf;
index index.html index.htm index.php;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] $status '
'"$request" $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
server_tokens off; ## 在錯誤頁面顯示nginx的版本號?
access_log logs/access.log main;
## 可以調tcp的參數
sendfile on; ## 類似splice,實現zerocopy
tcp_nopush on; ## TCP_CORK,等到tcp pkt到一定大小,再丟出封包
server_names_hash_bucket_size 128; # this seems to be required for some vhosts
## 壓縮
gzip on;
gzip_vary on; ## vary是cache的key的額外key
gzip_disable "msie6";
gzip_proxied any; ## 找有沒有壓縮過的
gzip_min_length 1000;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
server { # php/fastcgi
listen 80;
server_name domain1.com www.domain1.com;
access_log logs/domain1.access.log main;
root html;
location ~ \.php$ {
fastcgi_pass 127.0.0.1:1025;
}
}
server { # simple reverse-proxy
listen 80;
server_name domain2.com www.domain2.com;
access_log logs/domain2.access.log main;
# serve static files
location ~ ^/(images|javascript|js|css|flash|media|static)/ {
root /var/www/virtual/big.server.com/htdocs;
expires 30d;
}
# pass requests for dynamic content to rails/turbogears/zope, et al
location / {
proxy_pass http://127.0.0.1:8080;
}
}
upstream big_server_com { ## 做簡單的load balance,用rr
server 127.0.0.3:8000 weight=5;
server 127.0.0.3:8001 weight=5;
server 192.168.0.1:8000;
server 192.168.0.1:8001;
}
server { # simple load balancing
listen 80;
server_name big.server.com;
access_log logs/big.server.access.log main;
location / {
proxy_pass http://big_server_com;
}
}
}
Ref
nginx beginner full example advanced conf server name forwarded rewrite_module if is eval rewrite & query_string bunch of NGINX var list x-accel