前文我們聊了下Nginx作為web服務器配置https、日志模塊的常用配置、rewrite模塊重寫用戶請求的url,回顧請參考https://www.cnblogs.com/qiuhom-1874/p/12398242.html;今天來聊一聊Nginx是怎么反向代理,怎么防盜鏈;前文的最后我們提到了防盜鏈,到底什么是防盜鏈呢?在我們平時上網相信很多人都遇到過這樣的情況,我們打開一個網頁,在里面可以看到很多裂圖,看不到圖片,或者看到此圖片僅某某網站網友交流使用之類的,這就是防盜鏈;我們知道在一個網頁里面,里面的資源不一定都是來自一個服務器的,比如圖片很可能來自圖片服務器,js、css很可能來自其他靜態資源服務器上;所以稍微懂點的人就知道如何將別人網站上的圖片、js文件呀鏈接到自己的網站使用,這種行為就叫盜用別人家的資源,簡稱盜鏈;這里就不過多闡述了;我們來說說nginx的referer模塊吧。
一、ngx_http_referer_module:此模塊用於阻止對“Referer”頭字段中包含無效值的請求的站點訪問;
通常一次http事務就是客戶端請求服務端,服務端響應客戶端的一個流程;客戶端請求服務端,會在請求頭部添加一些信息,比如用什么方法請求服務端的資源呀,資源的路徑是什么,用的http協議版本是多少,請求的host主機是什么等等;其中如果客戶端是直接從瀏覽器上介入域名直接訪問web服務器,其頭部是沒有referer這個信息的;referer是什么?referer是記錄客戶端從哪里來訪問我們服務端的,如果客戶端是通過某個網站點擊訪問到我們的服務器時,它發過來的請求頭部就有對應網站的域名;防盜鏈就是利用referer這個頭部的信息來做控制的;
1、valid_referers none | blocked | server_names | string ...;定義合法referer合法值;這里解釋下,none表示請求頭部沒有referer字段,通常情況下沒有referer字段都是從瀏覽器(web客戶端)介入域名訪問的;blocked表示請求頭部有referer字段,但是沒有值,像這種請求我們是無法判斷客戶端是從哪里訪問我們服務器的,通常情況我們把這類請求時允許訪問的;server_name表示請求頭部有referer字段和信息,其值就是對方主機名;我們在定義一個合法的referer時,是可以用通配或正則去匹配server_names;
示例:
valid_referers none blocked server_names *.example.com example.* www.example.org/galleries/ ~\.google\.;
提示:以上配置表示合法的referer有 ,請求報文里沒有referer字段的請求,有referer字段但是沒有值的,以任何內容開頭結尾是.example.com的主機名或者是以example開頭的主機,或者referer是www.example.org/galleries/或者是包含google的都是合法的,意思是客戶端請求報文的referer信息滿足我們定義的合法信息,或者說能夠被我們定義的合法referer匹配到,我們就說該用戶是一個合法的請求,理所當然的是應該允許被訪問的;當然我們定義了合法referer,如果客戶端請求報文里的referer信息不配我們定義的合法referer匹配,我們就說這里客戶端的referer是非法的,是不被允許的,理所當然的就應該做其他處理;這個是ngxin里內部的機制,不被合法referer所匹配的referer都是非法的referer,通常是用$invalid_referer保留這些不合法referer;或者我們這樣理解,不被合法referer所匹配的請求報文就會被$invalid_referer所匹配;有了這種機制我們就可以明確定義那些請求時合法的,相對的那些請求是不合法的,對於不合法的我們可以這么處理;如下
提示:以上配置表示如果客戶端請求報文的referer信息不是.ilinux.com結尾或者不是以www.ilinux.開頭 或者 不是www.ilinux.io 或者不包含.baidu.或者.google. 我們都響應該客戶端請求響應碼為403;
二、ngx_http_proxy_module:此模塊允許將請求傳遞到另一個服務器。
1、proxy_pass URL;該指令主要作用是用來設置被代理服務器地址的,可以說主機名稱,IP地址加端口的形式;其中URL表示被代理服務器的地址,包含協議、主機名或IP加端口、URI等。傳輸協議通常是“http”或者"https";如果我們被代理的是一個本地unix-domain套接字時,也支持以http://或https://加unix套接字路徑的形式;如果我們代理的是一組服務器時,我們可以用upstream指令把該組服務器同一歸並為一個名稱的組服務器組,當然這是我們后面要聊的nginx作為負載均衡的配置;這里特別要說明的是URL中是否包含URI,什么意思呢,就是URL不包含URI的意思就是 被代理的URL沒有URI,就只有協議IP地址或域名或主機名,這種就叫不帶URI;帶URI就表示除了協議主機名或域名或IP地址外,后面還有RUI;對於這兩種情況Nginx處理邏輯上不一樣的,如果RUL不包含URI 那么nginx服務器不會改變源地址的URI;如果URL包含URI,nginx服務器將會使用新的URI替換原來的URI;
示例:
提示:以上配置就是我們所的URL不包含URI的情況,用戶請求www.test.com/en/docs/將會被該location匹配到,然后將訪問www.test.com/en/docs/將會被代理到http://nginx.org/en/docs/;我們可以理解為被代理的URL不包含URI時,Nginx服務器會把用戶請求的URI當作被代理服務器的URI;所以以上配置就表示,用戶訪問www.test.com/en/docs/將被代理至http://nginx.org/en/docs/
提示:在做以上實驗時,需要在Windows上做好解析www.test.com;Windows上需要在C:\Windows\System32\drivers\etc\hosts文件中添加一條解析記錄,語法同Linux里的hosts一樣192.168.0.30 www.ilinux.io www.test.com;
提示:以上配置就是URL包含URI的情況,這種情況Nginx服務器會把用戶請求的URI替換成被代理的URI;以上面的配置示例,如果用戶請求www.test.com/test/那么這個請求到了nginx服務器時,nginx會把用戶原有的URI/test/替換成/en/docs/,所以用戶請求www.test.com/test/就會被代理至http://nginx.org/en/docs/;
提示:通過上面的演示,我們可以總結為,如果我們不想改變源請求的URI,那么我們在后端代理時就不帶URI,如果我們想更改源請求URI,那么我們在后端代理時,就帶上URI即可
理解了上面我們所的URL包含或不包含URI,我們就不難理解下面的例子
示例:proxy_pass URL末尾是否帶“/”問題
提示:以上配置和我們之前的第一個示例就只多了一個“/”;多一個“/”在我們看來是不要緊,但它對nginx來說,意思卻變了,就以我們上面說的,這種就是URL包含URI的情況,nginx會把后面的“/”認為是URI,不是認為,它本來就是一個URI;當客戶端請求www.test.com/en/docs/時,nginx會把該請求代理至http://nginx.org/;當然這樣處理后的結果肯定和我們之前的結果是完全不一樣的,http://nginx.org/就表示請求nginx.org的主頁;
提示:和第一個示例一樣的URL,對於proxy_pass URL后面沒有"/"和有“/”被代理響應的結果是不一樣的;
除了上面URL包含或不包含URI問題需要我們特別注意外,我們還要注意,如果location定義URI時使用了正則,或在if語句或在limit_execept中使用了proxy_pass指令,則proxy_pass 之后不能使用URI;用戶請求時傳遞的URI將直接附加代理到的服務器之后;意思就是URL包含URI的情況不能在location 使用了正則匹配URL,或者URL包含URI的情況不允許用在if語句中 或limit_except中
示例:
提示:這種配置我們在語法檢查的時候就通不過,要想被通過,我們只需要把proxy_pass指令后面的URI去掉即可
提示:總結一點就是location中使用了正則匹配 URL時,后面代理是不能有URI的,否則語法錯誤;
2、proxy_set_header field value;設定發往后端主機的請求報文的請求首部的值;可用在http,server,location配置段中
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
提示:以上配置表示在用戶請求通過代理發送給后端主機時,在其請求頭部加上X-Real-IP這個字段,並且這個字段的值是$remote_addr(客戶端IP地址)和X-Forwarded-For字段,其值為$proxy_add_x_forwarded_for;$proxy_add_x_forwarded_for 這個變量是也是記錄IP地址的,不同的是,這個變量它記錄了客戶端IP和代理服務端ip,兩個IP分別用逗號隔開,如果沒有代理服務器的場景,這個變量的意義同$remote_addr是一樣的,都是記錄客戶端客戶端IP
3、proxy_cache_path:定義可用於proxy功能的緩存,此指令只可配置在http配置段;
語法:
proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
path:表示設置緩存數據存放路徑,該路徑必須事先存在;
levels;表示設置存放緩存數據的目錄級別,這個和前面說的nginx緩存目錄一樣。levels=1:2表示兩級目錄,且一級目錄是一個字符哈希目錄,二級目錄是兩個字符的哈希目錄,目錄名稱是基於URL哈希算法獲取到的;
keys_zone=name:size 表示設置緩存索引在內存區域的名稱和大小;
inactive=time設置非活動緩存時間,在指定的時間內如果該緩存項沒有被命中,nginx就會強制把該緩存從磁盤上刪除,如果下次有人訪問時在緩存,依次循環;默認10分鍾;
max_size=size:設置磁盤中緩存數據的大小限制,當緩存數據超過我們設定的大小時,就是用LRU算法來刪除緩存;
loader_files=number:設置緩存索引重建進程每次加載的數據元素的數量上限;
loader_sleep=time:設置緩存索引重建進程在一次遍歷結束、下次遍歷開始之間的暫停時長,默認是50ms
loader_threshold=time:設置遍歷一次磁盤緩存源數據的時間上限,默認設置為200ms
通常情況下我們不需要設置這么多選項,只需要把前三個選項設置好就行了,沒有特殊的要求后面的選項我們用默認值就可以
示例:
提示:以上配置表示定義代理緩存路徑是/cache/proxy/nginx 目錄級別是1:2:1 緩存索引重建進程內存區域名稱為proxy_cache,大小為10M 對於磁盤上的/cache/proxy/nginx/目錄最大緩存空間為2g;這樣設置后,我們就可以在各個server或location中來調用此緩存定義
4、proxy_cache zone | off;指明要調用的緩存,或關閉緩存機制;此指令可用於http,server,location配置段中;
示例
提示:這樣去調用緩存空間進行緩存是不能夠緩存的,因為我們調用緩存空間是有條件的,比如我們要對那些請求方法的請求進行緩存?對不同響應碼的資源緩存多久?是否在后端服務器出現錯誤時,我們繼續使用緩存來響應?所以我們現在雖然配置了調用緩存空間,但是我們服務器還是不知道怎么去緩存客戶訪問的內容;所以它干脆就不給緩存;
示例:我們只調用了緩存空間,沒有配置其他配置,用戶訪問的數據是否能夠緩存下來呢?
提示:可以看到我們只配置緩存空間然后調用是不行的,我們還需要指定緩存的key是什么 ,對客戶端使用的那些方法進行緩存,對不同的響應碼的資源緩存多久,這是調用緩存空間的幾個必要的配置,我們需要加上才行;
5、proxy_cache_key:定義緩存key,默認是$scheme$proxy_host$request_uri,它這個默認就是緩存的key是協議加代理主機地址或主機名或FQDN和用戶請求的uri當作緩存的KEY;也就是說服務端怎么去找緩存的方式,對應key的定義;
6、proxy_cache_methods METHODS:定義緩存用戶的請求方式,也就是說那些請求方法的資源我們要進行緩存,默認是GET HEAD;
7、proxy_cache_valid [code] time:定義不同的響應碼的資源緩存時長;
8、proxy_cache_use_stale error |timeout|……:定義后端服務器基於那種狀態使用緩存,默認是不基於后端服務器狀態使用緩存;比如后端服務器發生錯誤,是否用緩存中的內容響應客戶端?如果我們定義 proxy_cache_use_stale http 403就表示后端服務器如果響應代理服務器403,我們代理服務器就是用之前的緩存,響應客戶端;
示例:
提示:以上配置表示使用proxy_cache緩存空間,緩存key是用戶請求的uri進行緩存,對用戶使用GET 和HEAD方法請求的資源進行緩存,對響應碼是200 302的資源緩存15分鍾,對響應碼是404的資源緩存1分鍾,后端服務器出現500 或502的錯誤,代理服務器使用以前的緩存響應客戶端;
提示:可看到瀏覽器請求了兩個uri,在對應的緩存目錄里就存在兩個緩存項;這里面每一個緩存項就是對應一個用戶請求過多URI;通常情況我們啟用了Nginx代理緩存功能時,用戶第一次訪問就會很慢,但是只要把數據緩存下來后,后續的用戶在訪問相同的URI時,這個速度就會有明顯的提升;
總結對於nginx的緩存,我們首先在http配置段定義一個緩存空間,然后在各server或location中調用我們定義的緩存空間,並明確說明各種響應碼的資源緩存多長時間,對於proxy_cache_key 和 proxy_cache_methods是可以不指定的,不指定就代表使用默認值,從上面的配置我們其實就只定義響應碼是多少的資源緩存多久,其他的按照默認來,它也是可以進行緩存的;