天天看點

Nginx 内置變量,細化規則,真實IP擷取及限制連接配接請求你 Google 不到的配置 後記:根據url參數location

希望下周測試之後能用起來!!!感覺很有用的。

http://www.bzfshop.net/article/176.html

http://www.cr173.com/html/19761_1.html

http://blog.pixelastic.com/2013/09/27/understanding-nginx-location-blocks-rewrite-rules/

很多時候,我們的網站不是簡單的  普通使用者ie浏覽器  ——->  你的伺服器  的結構, 考慮到網絡通路速度問題,我們中間可能會有各種 網絡加速(cdn)。以本網站  www.bzfshop.net 為例,考慮到網站的安全性和通路加速,我們的架構是:

普通使用者浏覽器  —–>  360網站衛士加速(cdn,360防 cc,dos攻擊) ——>  阿裡雲加速伺服器(我們自己建的cdn,阿裡雲盾) —-> 源伺服器(php 程式部署在這裡,iptables, nginx 安全配置)

可以看到,我們的網站中間經曆了好幾層的透明加速和安全過濾, 這種情況下,我們就不能用上面的“普通配置”。因為上面基于  源ip的限制 結果就是,我們把 360網站衛士  或者  阿裡雲盾 給限制了,因為這裡“源ip”位址不再是  普通使用者的ip,而是中間  網絡加速伺服器 的ip位址。我們需要限制的是 最前面的普通使用者,而不是中間為我們做加速的 加速伺服器。

當一個 cdn 或者透明代理伺服器把使用者的請求轉到後面伺服器的時候,這個 cdn 伺服器會在 http 的頭中加入 一個記錄

x-forwarded-for :  使用者ip, 代理伺服器ip

如果中間經曆了不止一個 代理伺服器,像 www.bzfshop.net 中間建立多層代理之後,這個 記錄會是這樣

x-forwarded-for :  使用者ip, 代理伺服器1-ip, 代理伺服器2-ip, 代理伺服器3-ip, ….

可以看到經過好多層代理之後, 使用者的真實ip 在第一個位置, 後面會跟一串 中間代理伺服器的ip位址,從這裡取到使用者真實的ip位址,針對這個 ip 位址做限制就可以了,

取得使用者的原始位址

shell

1

2

3

4

5

6

7

8

9

10

11

map $http_x_forwarded_for  $clientrealip {

        ## 沒有通過代理,直接用 remote_addr

    ""    $remote_addr;  

        ## 用正則比對,從 x_forwarded_for 中取得使用者的原始ip

        ## 例如   x-forwarded-for: 202.123.123.11, 208.22.22.234, 192.168.2.100,...

        ## 這裡第一個 202.123.123.11 是使用者的真實 ip,後面其它都是經過的 cdn 伺服器

    ~^(?p<firstaddr>[0-9\.]+),?.*$    $firstaddr;

}

## 通過 map 指令,我們為 nginx 建立了一個變量 $clientrealip ,這個就是 原始使用者的真實 ip 位址,

## 不論使用者是直接通路,還是通過一串 cdn 之後的通路,我們都能取得正确的原始ip位址

很多時候,你在網上搜到一堆配置,你照着做了,但是你怎麼知道這個配置真的正确 ?是的,我們需要自己做一個有效的真實的測試,驗證它是正确的之後才真的采用它

nginx 這種配置怎麼測試呢? 用 echo 子產品,如果你知道 nginx 這個子產品的話。

以 www.bzfshop.net 網站為例, 我們首先測試這個 $clientrealip 是否真的是我們客戶機的 ip 位址,在網站上增加一個通路位址,比如  www.bzfshop.net/nginx-test,配置如下:

給 nginx 增加一個測試位址

server {

    listen   80;

        server_name  www.bzfshop.net;

        ## 當使用者通路 /nginx-test 的時候,我們輸出 $clientrealip 變量,看看這個變量

        ## 值是不是真的 使用者源ip 位址

        location /nginx-test {

                echo $clientrealip;

        }

接下來,用你的浏覽器通路  www.bzfshop.net/nginx-test,這個時候會彈出框下載下傳一個檔案 nginx-test,下載下傳完成用 notepad++ 打開,裡面就是一個 ip 位址

通路 www.ip138.com ,看看這個裡面記錄的ip位址是否和 ip138 偵測的ip 一緻?

通過這種方式,你就可以對 nginx 的一些複雜配置做有效的測試。

經過測試,我們确認 通過多層cdn 之後,$clientrealip 仍然是有效的原始使用者ip位址

下面是修改之後的 nginx 配置:

cdn環境下 nginx 的安全配置

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

## 這裡取得原始使用者的ip位址

    ""    $remote_addr;

## 針對原始使用者 ip 位址做限制

limit_conn_zone $clientrealip zone=totalconnlimitzone:20m ;

limit_conn  totalconnlimitzone  50;

limit_conn_log_level notice;

limit_req_zone $clientrealip zone=connlimitzone:20m  rate=10r/s;

#limit_req zone=connlimitzone burst=10 nodelay;

limit_req_log_level notice;

## 具體伺服器配置

    location ~ \.php$ {

                ## 最多 5 個排隊, 由于每秒處理 10 個請求 + 5個排隊,你一秒最多發送 15 個請求過來,再多就直接傳回 503 錯誤給你了

        limit_req zone=connlimitzone burst=5 nodelay;

        fastcgi_pass   127.0.0.1:9000;

        fastcgi_index  index.php;

        include    fastcgi_params;

    }    

通過上面的配置,現在你的網站可以完美的配合任何 網絡加速服務(cdn)的使用,并且同時能保證對“最終使用者的限制”。

寫這篇文章的原因是因為 我們最近把 www.bzfshop.net 遷移到  360網站衛士(wangzhan.360.cn)  上了,使用 360網站衛士 做我們的加速伺服器和安全保護,同時我們網站自身 nginx 本身也配置了防止攻擊的安全措施, 結果我們的安全配置把  360網站衛士的加速伺服器給 盾 掉了,因為所有使用者的通路都通過加速伺服器過來,很明顯加速伺服器超過了我們的“連接配接限制”。經過上面的改造之後,現在我們的 nginx 安全配置能夠和 360加速伺服器 完美配合,同時能對終端的使用者通路作限制。

寫下這些文字,希望對看到這篇文章的朋友會有用。

~~~~~~~~~~~~~~~~~~~~

經常需要配置nginx ,其中有許多以 $ 開頭的變量,經常需要查閱nginx 所支援的變量。

可能是對 ngixn資源不熟悉,幹脆就直接讀源碼,分析出支援的變量。

nginx支援的http變量實作在 ngx_http_variables.c 的 ngx_http_core_variables存儲實作:

ngx_http_core_variables

  1 static ngx_http_variable_t ngx_http_core_variables[] = {

把這些變量提取下,總結如下:

Nginx 内置變量,細化規則,真實IP擷取及限制連接配接請求你 Google 不到的配置 後記:根據url參數location
Nginx 内置變量,細化規則,真實IP擷取及限制連接配接請求你 Google 不到的配置 後記:根據url參數location
Nginx 内置變量,細化規則,真實IP擷取及限制連接配接請求你 Google 不到的配置 後記:根據url參數location

~~~~~~~~~~~~~~~~~~~~~~~~~~~

實際應用

如果作為代理伺服器,我們需要限制每個使用者的請求速度和連結數量,但是,由于一個頁面有多個子資源,如果毫無選擇的都進行限制,那就會出現很多不必要的麻煩,如:一個頁面有40個子資源,那麼如果想讓一個頁面完整的顯示,就需要将請求速度和連接配接數都調整到40,以此達到不阻塞使用者正常請求,而這個限制,對伺服器性能影響很大,幾百使用者就能把一台nginx的處理性能拉下來。

是以我們需要制定哪些請求是需要進行限制的,如html頁面;哪些是不需要限制的,如css、js、圖檔等,這樣就需要通過配置對應的location進一步細化。

我們不對css、js、gif、png,jpg等進行連接配接限制,而對除此之外的連結進行限制

http {

location配置簡單介紹:

文法規則: location [=|~|~*|^~] /uri/ { … }

= 開頭表示精确比對

~ 開頭表示區分大小寫的正則比對

~*  開頭表示不區分大小寫的正則比對

!~和!~*分别為區分大小寫不比對及不區分大小寫不比對 的正則

/ 通用比對,任何請求都會比對到。

多個location配置的情況下比對順序為(參考資料而來,還未實際驗證,試試就知道了,不必拘泥,僅供參考):

首先比對 =,其次比對^~, 其次是按檔案中順序的正則比對,最後是交給 / 通用比對。當有比對成功時候,停止比對,按目前比對規則處理請求。

~~~~~~~~~~~~~~~~~~

現在需要作如下的重定向:

<code>192.168.71.51/log.aspx –&gt; 192.168.80.147:8338/log</code>

<code>192.168.71.51/</code><code>do</code><code>.aspx –&gt; 192.168.80.147:8338/</code><code>do</code>

<code>192.168.71.51/uplog.aspx –&gt; 192.168.80.147:8338/log</code>

可以如下配置:

Nginx 内置變量,細化規則,真實IP擷取及限制連接配接請求你 Google 不到的配置 後記:根據url參數location
Nginx 内置變量,細化規則,真實IP擷取及限制連接配接請求你 Google 不到的配置 後記:根據url參數location
Nginx 内置變量,細化規則,真實IP擷取及限制連接配接請求你 Google 不到的配置 後記:根據url參數location
Nginx 内置變量,細化規則,真實IP擷取及限制連接配接請求你 Google 不到的配置 後記:根據url參數location

關于這裡的rewrite配置主要說明以下幾點:

 rewrite用法: rewrite 正則 替換 标志位

第一行配置和第二行配置順序不能颠倒,因為nginx會從上往下依次rewrite(break在這裡不起作用);

(?!)表示忽略大小寫比對(網上說的是~*,但好像不起作用,我的nginx版本是1.0.12);

 1,2表示前面正規表達式比對到的部分;

 rewrite可以在server裡也可以在location裡,nginx會首先執行server裡的rewrite,然後才會執行location,意味着location的是重寫後的url,之後還會執行location裡的rewrite,最後nginx還會拿結果去執行剩下的location。

實際開發中經常有根據請求參數來路由到不同請求處理者的情況,根據post請求參數需要些nginx插件,這裡主要簡單介紹下如何根據get參數來路由。

首先增加一個upstream,比如:

然後在location裡增加如下的判斷即可:

Nginx 内置變量,細化規則,真實IP擷取及限制連接配接請求你 Google 不到的配置 後記:根據url參數location
Nginx 内置變量,細化規則,真實IP擷取及限制連接配接請求你 Google 不到的配置 後記:根據url參數location
Nginx 内置變量,細化規則,真實IP擷取及限制連接配接請求你 Google 不到的配置 後記:根據url參數location
Nginx 内置變量,細化規則,真實IP擷取及限制連接配接請求你 Google 不到的配置 後記:根據url參數location

關鍵是标紅的行,$query_string表示url參數,後面是标準的正則比對,需要的注意的是nginx中if有很多限制,文法很苛刻,具體參看上面的文檔。

很簡單卻很實用的配置,希望能幫到正在找這方面資訊的同學。

i recently moved a cakephp website from an apache server to an nginx one. i had to translate url rewriting rules from one syntax to the other, and here is what i learned.

first of all, nginx internal logic for processing rewrite rules is not as straightforward as apache. in apache, rules are processed in the order in which they appear in your config file/<code>.htaccess</code>. in nginx, they follow a more complex pattern.

first of all, here are the (simplified) set of rules i had to convert :

the first rule deals with compressed <code>css</code> and <code>js</code> files. minified <code>css</code> and <code>js</code> files are saved in <code>/css/packed/</code> with a filename made of a md5 hash of the original filenames and a timestamp. so a url of<code>/css/packed_6e4f31ffc48b6_1330851887.css</code> will actually return the file located in<code>/css/packed/6e4f31ffc48b6_1330851887.css</code>

the second rule is about media files uploaded on the server. each uploaded file is stored in the <code>/files/</code> directory, in a subfolder made from the uploading date (like<code>/files/2012/08/25/</code>). the actual file is given a uuid when saved, and this uuid is used as its filename on disk. the rewrite rule allow the use of any custom filename when linking the file. this helps for seo purposes as well as making it more user-friendly when we present a download to our users. so <code>/files/2012/08/25/50483446-4b00-4d5b-8498-763e45a3e447/subscription_form.pdf</code> actually returns the file at<code>/files/2012/09/06/50483446-4b00-4d5b-8498-763e45a3e447.pdf</code>

and the last rule is the default cakephp rewrite rule. it first checks if the requested url points to an existing directory or file, and if not dispatch it to the main entry point :<code>index.php</code> with the requested url as a parameter.

rewrite rules in nginx are usually found in <code>location</code> blocks. there are several ways you can define a <code>location</code> block, and it affects the order in which the rules will be parsed.

nginx first checks for <code>location =</code> blocks. those blocks are used to catch an exact match of the requested url. once such a block is found, its content is applied, and nginx stops looking for more matches.

in this example, a request for <code>/my-exact-file.html</code> will be redirected to<code>http://external-website.com.</code> note that you need to repeat the url in both the<code>location =</code> block and the <code>rewrite</code> rule.

the <code>location =</code> is of very limited use as it only accepts an exact match on a string. much more useful are the <code>location ~</code> blocks that performs matches on regex (and the <code>location ~*</code> for a case-insensitive version).

such blocks are tested after the <code>location =</code>ones, in the order they appear in your configuration file. once a block matches, nginx applies its content but does not stop. it keeps looking for other blocks that might match and apply them. it's up to you, in the block content, to define if the parsing should stop, using the <code>break</code> command.

in the first rule i'm looking for any <code>/css/packed_*</code> or <code>/js/packed_*</code> request, and converting them to <code>/css/packed/*</code> or <code>/js/packed/*</code>. note the use of backreferences in the rewrite using <code>$x</code> variables. in the second rule i simplified the original regex from apache to catch the <code>/2012/08/23/</code> in <code>$1</code>, the uuid in <code>$2</code>, the filename in <code>$3</code> and the extension in <code>$4</code> and rewriting the request to the correct file on disk.

both rewrites ends with the <code>break</code> flag. it tells nginx that it should stop looking for other <code>location ~</code> blocks matching the requested url and just serve the file. another useful flag is <code>last</code>, which tells nginx to restart its whole url matching process from the beginning but this time using the newly rewritten url.

there is one last <code>location</code> block that we can use, and it's the simple <code>location</code>, without any prefix. these <code>location</code> blocks will be checked last, if no <code>location =</code>or <code>location ~</code> had stopped the processing. they are especially good for a last "catch all" solution, and we are going to use them to dispatch urls to <code>index.php</code>

using <code>location /</code>, we'll catch any remaining requests. the <code>try_files</code> command will test every one of its arguments in order to see if they exist on disk and serve them if they do. so in our example it will first check for the requested uri, and if such a file exists, will serve it. otherwise it will simply dispatch it to the main <code>index.php</code> with the requested url as an argument and cakephp will do the rest.

there is one last thing we must do, it's telling nginx to pass any <code>.php</code> file to the php fastcgi. this is quite easy using a <code>location ~</code> block matching any <code>.php</code> file. this will even apply to files served through <code>try_files</code>.

wrapping your mind around the order in which nginx applies your rewrites is not easy at first. i hope this post helped you making sense of it.

note that there also is the <code>location ^~</code> block but i found it to be of very limited used as its behavior can be replicated with the more generic <code>location ~</code> blocks.

繼續閱讀