Nginx學習:目錄索引、字符集與瀏覽器判斷模塊
今天要學習的內容有幾個還是大家比較常見的,所以學習起來也不會特別費勁。對于目錄的默認頁設置大家都不會陌生,字符集的設置也比較常見,而瀏覽器的判斷這一塊,可能有同學會接觸過針對 IE 瀏覽器的一些特殊設置。今天的內容也基本上都是可以在 http、server、location 中可以進行配置的,只有一個指令是只能在 http 中配置的,到時候會單獨說。
默認頁 index
沒錯,就是那個最常見的 index 指令,它是單獨的屬于 ngx_http_index_module 模塊,不需要編譯,是核心源碼中的一個配置指令。不管是默認的 nginx.conf 還是用一些面板工具安裝的 Nginx ,都會有這個指令的出現。
location?/?{root???html;index??index.html?index.htm;
}
這就是默認配置中給的 index 指令的配置,如果是 PHP 環境,通常會把 index.php 放在前面。它的配置參數非常簡單。
index?file?...;
默認值是 index.html ,所以如果是靜態頁面,并且本身就有 index.html 這個文件的話,不配這個指令也行。它的作用就是定義如果是以 / 結尾的 location 路徑,也就是目錄路徑,就會按照配置的順序檢查文件。比如上面的默認配置中,如果在 root 指定的根目錄下沒有 index.html 文件,就會找 index.htm 文件。如果有 index.html ,就直接會返回 index.html 文件了。那么如果這兩個文件都找不到呢?404 唄。不過它還可以在最后一個元素帶上一個有路徑的文件,比如 /index.html 。現在我們就來配一個。
location?/indextest/?{index?index.html?aaa.html?a.txt?/index.html;
}
注意,我們要在 root 指定的目錄下創建一個 indextest 目錄,否則即使最后有一個 /index.html ,也會一直是 404 。
mkdir?/usr/local/nginx/html/indextest
現在這個目錄里面沒有配置中指定的這些文件,直接訪問的話,就會返回根目錄的首頁文件。
接著我們可以一個一個地創建,比如先創建一個 a.txt ,再次訪問,就會顯示 a.txt 中的內容。這里的文件是可以自由定義的,不僅是 html 或者 txt 文本,php 之類的腳本文件都是可以的,前提是也要配好 FastCGI 。
另外需要注意的是,index 文件會引發內部重定向,請求可能會被其它 location 處理。 比如,下面這個例子:
location?=?/?{index?index.html;
}location?/?{...
}
請求 “/” 實際上將會在第二個 location 中作為 /index.html 被處理。這一段是官網的例子,我沒有測試,其實就是 location 規則的問題。大家可以自己試試,意思就是 / 會變成 /index.html ,然后交由下面的 location 進行處理。
目錄索引
有的時候,我們不指定 index ,而是需要直接列出整個目錄里面的文件內容,就像一些開源代碼的下載地址。比如說 Hadoop 的下載地址:https://archive.apache.org/dist/hadoop/common/
在 Apache 中,也有類似的設置,而在 Nginx 中,只需要啟用 ngx_http_autoindex_module 模塊就可以了。這個模塊也是核心源碼中的,直接就可以使用。首先,還是要準備一些文件,比如可以用 shell 直接生成一些。
echo?"this?is?1.txt"{1..99}??>>?/usr/local/nginx/html/indextest/1.txt
然后,就簡單地配置一下,還是使用上面的 indextest 目錄。
location?/indextest/?{#index?index.html?aaa.html?a.txt?/index.html;autoindex?on;
}
接下來直接訪問 /indextest/ 路徑,就可以看到下面的內容。
直接點擊文件就可以打開訪問對應的文件啦。
autoindex
開啟或者關閉列出目錄中文件的功能。
autoindex?on?|?off;
默認是 off ,當然是不能隨便把目錄中所有的文件信息都暴露出來啦,因此,大部分情況下都只是特殊的一些目錄才會用到。如果是 off ,并且沒有 index ,也沒有 index.html 的話,那就是 404 。
autoindex_exact_size
設置目錄中列出的文件是顯示精確大小,還是對KB,MB,GB進行四舍五入。
autoindex_exact_size?on?|?off;
默認是 on ,也就是精確顯示大小,設置成 off 的話,就會進行四舍五入帶單位的顯示。同時,它還必須是下面的 autoindex_format 設置為 html 時才會有效果。最后我們會一起看例子。
autoindex_format
設置索引目錄展示的形式。
autoindex_format?html?|?xml?|?json?|?jsonp;
默認是 html ,就是我們上面看到的那個樣子,還可以換成別的形式。后面我們一起測試時看效果。
autoindex_localtime
設置目錄中列出文件的時間是本地時間還是UTC時間。
autoindex_localtime?on?|?off;
默認值是 off ,使用的是 UTC 時間,改成 on 之后,就是服務器的時間。
綜合測試
現在直接配置上除上面的例子中沒用到的那三個指令吧,如果是 on 的就換成 off ,off 的就換成 on ,html 也換成 json 。
autoindex_exact_size?off;autoindex_format?json;autoindex_localtime?on;
看看效果,輸出的結果是不是變成了下面這樣的 json 格式了。
但是大家會發現,size 表示的文件大小沒有變化呀,前面說過了,autoindex_exact_size 只有在 html 的時候才會有效果。因此,我們還是要把 autoindex_format 換回 html ,這樣就可以看到大小顯示的結果也不一樣了。
字符集 Charset
字符集的模塊全名是 ngx_http_charset_module 模塊。記得剛開始學 PHP 的時候,還是 PHP 剛剛升級到 5 的時候,5.2 或者 5.1 是主流版本,MySQL 也是同樣的剛到 5 左右的版本。在哪個時代,Discuz 或者 DedeCMS 這些開源程序都會提供 UTF8 和 GBK 兩種版本,是不是感覺怪怪的,因為當時 UTF8 還沒有一統天下嘛。
到了現在,基本上不管是什么軟件,PHP的編輯器創建的文件、MySQL建庫建表、Redis、Go語言、Java、前端,等等等等,默認全是 UTF8 了,因此,字符集會產生的亂碼問題說實話已經大大減少了。包括在 Nginx 中,默認的配置文件中那個默認的 server 里面,第四行就是一個被注釋掉的?#charset koi8-r;
配置。能寫在默認配置文件中,說明還是比較常用的,而被注釋掉了,說明現在默認可能并不是完全需要了。總之,我們不敢說它一定是很重要,但確實是一個不能忽略的配置項。
咱們的測試,就設置一個 gbk ,讓 utf8 亂碼好了,默認的 koi8-r 我都不知道是哪里的語言編碼....
charset?gbk;
繼續復用上面的 /indextest/ 這個路徑,在 location 添加上面的這條配置指令。然后隨便建立一個文件,并且在里面寫中文,看看訪問結果是不是亂碼了。其實呀,這個指令的意思是在響應頭的 Content-type 中添加上 charset 參數。
默認情況下,我們訪問的普通 Nginx 靜態頁面返回的 Content-type 就是一個 text/html ,而設置了 charset 之后,就變成了?text/html;charset=gbk
?了。現在大部分情況下不需要設置,如果不設置,默認瀏覽器就會使用 UTF8 ,同時訪問的文件也是 UTF8 的,這樣就沒啥問題了。另外如果是 PHP ,除去文件編輯器和普通創建的文件都會使用 UTF8 編碼外,很多框架以及我們自己寫 Demo 時,也會這樣加上一段。
//?php文件
header("Content-type:?text/html;?charset=utf-8");
當使用代理或者 FastCGI 時,默認情況下會直接使用它們返回的響應頭中的 Content-type 。
charset
為響應頭的 “Content-Type” 添加指定的字符集。
charset?charset?|?off;
現在的默認值就是 off ,表明不在響應頭的 “Content-Type” 中添加字符集。如果這個字符集和 source_charset 指令設置的字符集不同,就會進行轉換。這個指令后面馬上就會說。
它的參數也可以使用變量來指定,參數可以使用的值需要符合 charset_map 或 source_charset 的值。此外,字符集也可以在響應頭的 “X-Accel-Charset” 中設置。 這個功能可以使用 proxy_ignore_headers和 fastcgi_ignore_headers 指令來禁用。
charset_map
描述了從一個字符集到另一個字符集的轉換表。這個配置指令只能配置在 http 模塊中,不能放到 server 或 location 里面。
charset_map?charset1?charset2?{?...?}
默認值是空的,意思就是從 charset1 到 charset2 的轉換,比如說 UTF8 到 GBK 的轉換規則,配置比較復雜,比如這樣:
charset_map?koi8-r?utf-8?{C0?D18E?;?#?small?yuC1?D0B0?;?#?small?aC2?D0B1?;?#?small?bC3?D186?;?#?small?ts...
}
我們下載的安裝包中已經提供了從koi8-r
到?windows-1251
,從koi8-r
到utf-8
,以及從?windows-1251
到utf-8
的完整轉換表。都不是我們常用的,這一塊大家了解一下就好啦。
charset_types
使模塊在響應時能處理除了 “text/html” 之外其他指定的MIME類型。
charset_types?mime-type?...;
默認值包含 text/html text/xml text/plain text/vnd.wap.wml application/x-javascript application/rss+xml 這些。這個配置其實和我們上篇文章中學過的 addition_types 一樣,就是說這些類型的會讓 charset 指令產生效果,如果有特殊需求可以再繼續添加。
override_charset
對于接收到的代理服務器或者 FastCGI 服務器的響應頭中 “Content-Type” 已經帶有字符集的情況,確定是否進行字符集轉換。
override_charset?on?|?off;
默認值是 on ,如果開啟轉換,接收到的響應中指定的字符集會被當作原始字符集。如果在子請求中接收到的應答,始終會將應答的字符集轉換為主請求的字符集,無論 override_charset 指令是否開啟。
我拿 PHP 試了,沒啥效果,會直接報 500 錯誤,PHP 中使用 header 指定字符集為 UTF8 ,然后 Nginx 中設置的 Charset 還是 gbk 。錯誤信息是指定的字符集在 charset_map 或 source_charset 中不存在。也沒找到合適的 UTF8 轉換 GBK 的 charset_map 文件,所以這一塊也就不知道怎么繼續測試啦。有了解的小伙伴記得評論留言哦。
source_charset
定義響應中的原始字符集。
source_charset?charset;
默認是空的,沒有設置。如果這個指令配置的字符集與 charset 配置的不一樣,就會進行轉換。
瀏覽器判斷 browser
在瀏覽器判斷的 ngx_http_browser_module 模塊中,有一個變量你肯定見過,那就是?$msie
?這個變量。用于判斷當前請求過來的瀏覽器是不是 IE 瀏覽器,如果是的話,這個變量的值就會變成 1 。
除了這個變量之外,這個模塊里面還提供了三個變量,另外還有幾個指令,是對這幾個變量進行配置以及賦值的。也就是說,整個模塊其實都圍繞下面的這三個變量。
$modern_browser
?如果瀏覽器被為識別為新式瀏覽器,該值等于 modern_browser_value 指令設置的值。$ancient_browser
?如果瀏覽器被識別為舊式瀏覽器,該值等于 ancient_browser_value 指令設置的值。$msie
?如果瀏覽器被識別為任何版本的MSIE,該值等于 “1” 。
這些變量有啥用呢?別急,后面測試的時候再看,先來看看在這個模塊中的配置指令,一共就四個。
ancient_browser
如果任一指定的子串在請求頭的“User-Agent”域中被發現,瀏覽器將被認定為舊式瀏覽器。
ancient_browser?string?...;
它的默認值是空的,沒有默認值,而另外一個 ancient_browser_value 配置的默認值是 1 ,也就說,如果使用普通的 if 不帶等于的進行布爾判斷,那么?$ancient_browser
?將一直返回 1 。這個我們后面測試時會見到效果。
ancient_browser_value
設定變量?$ancient_browser
?的值。
ancient_browser_value?string;
上個指令中就介紹過了,它的默認值是 1 ,當然也可以設置成別的值,而且不一定是數字,字符串也可以。可以用在 if 判斷上。但是如果直接設置了這個值,那么后續任何別的操作都沒用了,$ancient_browser
?會一直是這個配置指令設置的值。
modern_browser
指定一個版本,此版本及后續版本的瀏覽器都被認定為新式瀏覽器。
modern_browser?browser?version;
modern_browser?unlisted;
瀏覽器可以是下列之一: msie, gecko (基于Mozilla), opera,safari, 或者 konqueror 。注意這里沒有 Chrome 哦,Chrome 也是基于 Mozilla 的,因此用 gecko 就可以了。
版本可被指定為以下形式:X, X.X, X.X.X, 或 X.X.X.X。 每一形式的最大值分別是4000, 4000.99, 4000.99.99, 和 4000.99.99.99 。
如果瀏覽器既沒有在 modern_browser 中列出,又沒有在 ancient_browser 中列出時,并且配置了特殊值 unlisted ,那么瀏覽器將被認定為新式瀏覽器,否則認定為舊式瀏覽器。 如果請求頭中沒有 “User-Agent” 域,瀏覽器以沒有列出對待。
看著有點懵吧?其實它就是和 ancient_browser 反過來的,馬上在例子中我們就可以看到效果。
modern_browser_value
設定變量?$modern_browser
?的值。
modern_browser_value?string;
和 ancient_browser_value 的意思是一樣的,它的默認值同樣也是 1 ,但是,注意 modern_browser 定義中說的那些內容,如果是默認情況下,modern_browser 是空的,即使你下載最新版本的瀏覽器也不會判斷為新式瀏覽器,因此,最終?$modern_browser
?的值在默認情況下其實是個空的。
測試 browser
看上面的變量和配置項有點暈吧?其實這一塊就是幫助我們用自己定義的標準來判斷指定的瀏覽器,將這個瀏覽器歸屬到 新式 或 舊式 瀏覽器這兩個陣營中。然后使用兩個變量就可以通過 Nginx 中的編程語法,比如 if 來進行一些特殊的判斷。
location?/browsertest/?{add_header?Content-Type?text/plain;#ancient_browser_value?0;modern_browser?safari?600.0.0;ancient_browser?safari;if?($ancient_browser)?{#return?301?/indextest/1.txt;}return?200?$modern_browser,$ancient_browser,$msie;
}
在上面的例子中,先不看注釋的部分。modern_browser 我們設置了一個 safari ,版本號是 600.0.0 ,意思就是只要是大于等于這個版本的 safari 瀏覽器,就會判定為新式瀏覽器,$modern_browser
?會變成 1 ,而?$ancient_browser
?會變成空的字符串。反過來,如果不是 safari 瀏覽器,或者版本小于指定的版本號,上面兩個變量的值就會反過來。最后,我們通過 return 直接打印這三個變量的值。
怎么測試呢?我們可以通過 Postman 來重寫請求頭的 User-Agent 字段實現。
這一大串的字符怎么來的?拿真的瀏覽器請求一下,然后用開發者工具查看請求頭復制出來嘛。然后我們就好操作了,直接更改這一段字符串中最后的那個版本號標識就好啦。
截圖中的這個版本號,返回的結果是 ",1," 這樣的內容。而如果我們改成 600.1.15 ,結果就會變成 "1,," 這樣。
接著,我們可以打開上面代碼中 if 代碼段內的注釋,如果判定為 舊式 瀏覽器,那么就直接 return 進行 301 跳轉到最開始的 indextest 目錄,并打開 1.txt 文件。這就是模擬我們可能會對一某些瀏覽器有一些特殊的操作。如果判定不是 舊式 瀏覽器的話,還是正常返回那三個變量的值。這就是變量在 Nginx 編程中的重要作用。
這個 if 是不是很像我們的編程語法呀?但其實,它也是個配置指令,或者你可以將整個 Nginx 中的配置指令都看成是一種編程語言的語法。就像 HTML 是超文本標記語言一樣,Nginx 的配置指令可以說也是構建在主體的 C/C++ 程序之上的一套配置標記語言。關于 if 更詳細的內容,在后面的重寫模塊中會再詳細的學習。
最后上面那個 ancient_browser_value 注釋大家也可以打開試下,不出意外的話,現在你怎么修改版本號,怎么配其它的指令,一直都會是 ",0," 這樣的返回結果。
總結
沒騙大家吧?index 和 charset 肯定是大家比較常見的配置指令。而且是非常常見,index 在默認的配置文件中就有,而 charset 雖然沒有直接給出,但在默認配置文件的注釋中也能看到,這就說明它也真的是非常常用的一種配置指令。另外就是?$msie
?這個變量估計不少同學是見過的,也可能在你的項目就就會對 IE 進行一些特殊的處理。
好了,繼續接下來的學習吧,這樣刷文檔的感覺咋樣?反正我現在發現官方文檔真的是我們的第一學習神器哦!
參考文檔:
http://nginx.org/en/docs/http/ngx_http_index_module.html
http://nginx.org/en/docs/http/ngx_http_autoindex_module.html
http://nginx.org/en/docs/http/ngx_http_charset_module.html
http://nginx.org/en/docs/http/ngx_http_browser_module.html