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/htmltext/xmltext/plaintext/vnd.wap.wmlapplication/javascriptapplication/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命令:

map $sent_http_content_type $charset {
    default '';
    ~^text/ utf-8;
    application/javascript utf-8;
    application/rss+xml utf-8;
}

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

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

charset_types *;

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

charset $charset;

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