Nginx是一個免費開源且穩定高效的Web伺服器程式,經常用來提供HTTP網頁和靜態檔案等資源。但是有些人在使用Nginx的時候會碰到網頁或是文字檔用瀏覽器開啟時出現亂碼的問題,這該怎麼解決呢?



如下圖,在瀏覽器上開啟文字檔時,瀏覽器卻顯示了一堆亂碼。

nginx-utf8

當遇到這個問題的時候,可以先開啟瀏覽器自帶的網頁檢查工具,查看伺服器是否有回應「Content-Type」標頭。如上圖,伺服器確實有回覆「Content-Type」標頭,內容如下:

text/plain

預設情況下,Nginx可以辨識常見的檔案副檔名,來決定「Content-Type」標頭的內容。但是Nginx自動填寫的「Content-Type」標頭,只會有Mime類型的部份,並不會有「charset」(字元集)的部份。因此只知道文件的Mime類型卻不知道其文字使用的編碼格式為何的瀏覽器,通常會直接使用「ISO-8859-1」這個編碼格式對其解碼,如果該文件本來就不是使用「ISO-8859-1」來編碼的,錯誤解碼後,瀏覽器就會顯示出亂碼啦!

為了解決這個問題,我們必須要讓Nginx伺服器告訴瀏覽器文件使用的編碼方式,也就是要在「Content-Type」標頭中加上「charset」的設定值。至於文件到底要使用什麼編碼方式比較好,其實也不用多想,就是用「UTF-8」,沒有例外!「UTF-8」是大部份程式語言內建的字串編碼方式,它只需要用很小的空間,就可以表達出世界上大部份語言所使用的文字。

所以要如何讓Nginx伺服器能在回應純文字文件的時候,在「Content-Type」標頭中自動加上「charset」的設定值呢?

我們需要修改Nginx的設定檔,在「http」區塊或是其底下的「server」或是「location」區塊中使用Nginx提供的「charset」命令,直接指定「Content-Type」標頭的「charset」的設定值為「utf-8」。

charset utf-8;

如此一來,就可以讓「charset_types」命令所指定的Mime類型,都在「Content-Type」標頭中自動加上「charset=utf-8」這個設定值。有了正確的「charset」的設定值後,瀏覽器就不會再出現亂碼啦!

nginx-utf8

您可能會有疑問:「『charset_types』命令所指定的Mime類型」到底有哪些呢?Nginx預設能夠在「Content-Type」標頭中使用「charset」設定值的Mime類型為「text/html」、「text/xml」、「text/plain」、「text/vnd.wap.wml」、「application/javascript」和「application/rss+xml」,也就是說,如果文件的Mime類型不在上述之列,就算使用「charset」命令,也不能夠在「Content-Type」標頭中加上「charset」設定值。

如果需要自訂能夠使用「charset」設定值的Mime類型,我們需要自行撰寫「charset_types」命令。「charset_types」命令的撰寫方式如下:

charset_types text/html text/xml text/plain text/vnd.wap.wml application/javascript application/rss+xml;

以上命令是Nginx的預設值,可以看到預設值並不包含所有的純文字文件。所以如果我們要讓「charset」設定值能夠套用在所有純文字文件上,是不是只要把上面的命令改成以下這樣呢?

charset_types text/* application/javascript application/rss+xml;

以上命令看起來可行,也可以通過Nginx設定檔的檢查,但是「charset_types」命令中的「text/*」並不會真的具有「星號表示所有類型」的功能。如果使用這個命令的話,所有以「text/」開頭的Mime類型的文件都無法套用到「charset」設定值。

為了解決這個問題,讓Nginx伺服器真的可以在回應所有類型的純文字文件時,在「Content-Type」標頭中自動加上「charset」的設定值,我們必須使用到「map」命令和「$sent_http_content_type」變數。Nginx的「map」命令,可以判斷某值究竟是什麼值,來輸出另一個值到其它的變數。而「$sent_http_content_type」變數,則是儲存目前要回應的「Content-Type」標頭之內容。

結合之後,可以撰寫出如下的「map」命令:

以上命令,當原本Nginx伺服器要回應的「Content-Type」標頭的內容為「application/javascript」、「application/rss+xml」或是以「text/」開頭時,就可以讓「$charset」變數的值設為「utf-8」;否則的話就設為空字串「""」(同off)。

接著使用「charset_types」命令,將允許的Mime類型改為單獨的星號「*」,使所有的Mime類型都可以被「charset」命令控制。如下:

charset_types *;

然後再使用「charset」命令,套用「$charset」變數的內容給「charset」設定值。如下:

charset $charset;

如此一來,就能真正讓Nginx伺服器在回應所有類型的純文字文件時,在「Content-Type」標頭中自動加上「charset」的設定值了!