nginx配置的常見陷阱及錯誤

不論是新老用戶均可能掉進一樣的陷阱裏。下面咱們列舉了覺的問題同時也給出瞭解決辦法。在Freenode的IRC頻道#nginx裏,咱們常常遇到這類問題。javascript

導言

這些常見問題,通常是因某人試圖從別人的配置裏直接拷貝後拿來用,並非全部拷貝的都有問題,惋惜的是,大部分都有問題。即便是Linode文檔庫裏也存在因社區成員不去維護致使質量差的信息。php

這個文檔由大量NGINX社區成員建立和審查,這個特殊文檔的存在也是由於這些太過來常見了並常常發生。css

若是你的問題並未列明

你可能並無找你跟你直接有關的特殊問題,也許咱們確實是沒有指出你常常的特殊問題。但,不要覺得你來到這個頁面是沒有緣由的,你被帶到這個頁面至少表示你在下面的問題裏遇到過問題:)html

Chmod 777

永遠不要使用777。不要覺得這只是一個漂亮的數字,即便在測試環境也代表你會沒有線索查你正在作的事。你能夠對整個路徑的權限進行檢查,能夠考慮你正在作的事實。前端

要檢查路徑的權限,你能夠:java

namei -om /path/to/check

Location 裏的 root 配置

劣:node

server {
    server_name www.example.com;
    location / {
        root /var/www/nginx-default/;
        # [...]
      }
    location /foo {
        root /var/www/nginx-default/;
        # [...]
    }
    location /bar {
        root /var/www/nginx-default/;
        # [...]
    }
}

能夠工做。每一個location塊裏都加上root指令沒有錯,也能很好的工做。那會有什麼問題呢?若是某一個location塊你路徑寫錯了,將致使沒有根目錄。來看下什麼是優的配置。nginx

優:正則表達式

server {
    server_name www.example.com;
    root /var/www/nginx-default/;
    location / {
        # [...]
    }
    location /foo {
        # [...]
    }
    location /bar {
        # [...]
    }
}

多個Index指令

劣:後端

http {
    index index.php index.htm index.html;
    server {
        server_name www.example.com;
        location / {
            index index.php index.htm index.html;
            # [...]
        }
    }
    server {
        server_name example.com;
        location / {
            index index.php index.htm index.html;
            # [...]
        }
        location /foo {
            index index.php;
            # [...]
        }
    }
}

爲什麼重複那麼多沒必要要的行?其實只要簡單的使用index指令一次,只要在http{}裏使用一次,下面的區塊將自動繼承。

優:

http {
    index index.php index.htm index.html;
    server {
        server_name www.example.com;
        location / {
            # [...]
        }
    }
    server {
        server_name example.com;
        location / {
            # [...]
        }
        location /foo {
            # [...]
        }
    }
}

使用if指令

只有少數一些討論if的文章,有篇叫 if是魔鬼 的你必定要看,下面舉些列子說明爲什麼if如此壞。

server_name指令與if

劣:

server {
    server_name example.com *.example.com;
        if ($host ~* ^www\.(.+)) {
            set $raw_domain $1;
            rewrite ^/(.*)$ $raw_domain/$1 permanent;
        }
        # [...]
    }
}

這裏實際上有三個問題。首先看看if,也是咱們如今要關注的,爲什麼是劣的?你讀過這篇文章 If是魔鬼 嗎?當nginx接收到一個請求無論子域名是什麼,即便是www.example.com或example.com,if指令也是每次都要調用,由於NGINX須要每次對請求頭Host作處理,這特別沒有效率,你要千萬注意避免它。能夠替換爲兩個server區塊,以下面的列子:

優:

server {
    server_name www.example.com;
    return 301 $scheme://example.com$request_uri;
}
server {
    server_name example.com;
    # [...]
}

這種配置也可使配置更好閱讀,這種方法減小了NGINX的處理需求。咱們去除了謬誤的if,同時咱們使用了$schema避免了硬編碼URI,如http或https。

檢查文件是否存在(if)

使用if來判斷一個文件是否存在太可怕了。也就是說若是你有最新版的NGINx,使用tryfiles指令將更加容易。

劣:

server {
    root /var/www/example.com;
    location / {
        if (!-f $request_filename) {
            break;
        }
    }
}
優:
server {
    root /var/www/example.com;
    location / {
        try_files $uri $uri/ /index.html;
    }
}

咱們只是修改了判斷文件是否存在但又不使用if指令,使用tryfiles表示能夠測試一系列文件,若是$uri不存在,再試$uri/,若是還不存在,就使用默認位置(index.html)。

 

在這個列子,若是$uri存在就使用它,若是不存在,再判斷$uri/目錄是否存在,若是不存在就使用index.html,必需要保證最後一個檢測的文件存在。很簡單吧!下面是另外一個例子去掉if指令。

WEB應用的前端控制模式

"前端控制模式"-Front controller patterm在php開源界很是流行,但大都使用的比較複雜,如Drupal、jammla等,只要像這樣:

try_files $uri $uri/ /index.php?q=$uri&$args;

注意-參數根據你使用的開源軟件不一樣而不一樣。如:

  • "q"是Drupal,Joomla,Wordpress的參數
  • "page"是CMS Make Simple的參數

有些軟件甚至不須要查詢參數而是用REQUEST_URI參數,如,Wordpress支持這個:

try_files $uri $uri/ /index.php;

若是你不關心檢查目錄,只要去掉$uri/。

固然,你可能遇到些特殊狀況,須要更復雜的配置,但對通常站點,這些就夠了。

傳遞未受控的請求到PHP

不少NGINX+PHP的配置會傳遞全部以.php結尾的請求給php,但這裏會出現一個嚴重的安全問題,可能致使精心構造的代碼被執行。

這類問題看起來如:

location ~* \.php$ {
    fastcgi_pass backend;
    # [...]
}

這裏,每一個以.php結尾的請求將傳遞給FastCGI後端。這會致使一個問題,PHP的默認配置對於找不到的文件,會對整個請求文件路徑猜想哪一個部分須要執行。

舉例,若是請求是/forum/avatar/1232.jpg/file.php,文件並不存在,但/forum/avatar/1232.jpg存在,則php也會執行1232.jpg,若是1232.jpg包含php代碼,也會被執行。

避免執行的選項有:

  • 在php.ini設置cgi.fixpathinfo=0.這會致使php不嘗試字面上的路徑,若是文件沒找到就中止處理。
  • 保證NGINX只傳遞特定文件給PHP
location ~* (file_a|file_b|file_c)\.php$ {
    fastcgi_pass backend;
    # [...]
}
  • 指定用戶上傳的目錄不能夠執行php
location /uploaddir {
    location ~ \.php$ {return 403;}
    # [...]
}
  • 使用tryfles指令過濾有問題的條件
location ~* \.php$ {
    try_files $uri =404;
    fastcgi_pass backend;
    # [...]
}
  • 使用嵌套location過濾
location ~* \.php$ {
    location ~ \..*/.*\.php$ {return 404;}
    fastcgi_pass backend;
    # [...]
}

腳本文件中的FastCGI路徑

太多例子說用絕對路徑來指定路徑的錯誤作法。一般見於php配置塊。當你從svn中安裝NGINX,一般會有include fastcgiparams;在你的配置中,目錄通常在/etc/nginx。

優:

fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;

劣:

fastcgi_param  SCRIPT_FILENAME    /var/www/yoursite.com/$fastcgi_script_name;

$docmentroot在哪設的?一般是在你的server塊裏,不在?看看上面的第一個坑。

費力的Rewrites

在這不要犯糊,很容易被正則表達式搞蒙,事實上,很簡單,只要努力作到足夠簡單就行。

劣:

rewrite ^/(.*)$ http://example.com/$1 permanent;

優:

rewrite ^ http://example.com$request_uri? permanent;

更優:

return 301 http://example.com$request_uri;

看看上面的例子,第一個規則是獲取所有url而後減fcfu最一個反斜杆,事實上使用內置變量$request_uri能夠更簡單。

Rewrite丟失的http://

很是簡單,rewrites規則都是相對路徑,除非你告訴NGINX不是,使用rewrite的絕對路徑也很簡單,添加一個schema。

劣:

rewrite ^ example.com permanent;

優:

rewrite ^ http://example.com permanent;

上面的例子中只是添加了http://,簡單而有效。

代理 全部內容

劣:

server {
    server_name _;
    root /var/www/site;
    location / {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/tmp/phpcgi.socket;
    }
}

噁心。這個例子中,什麼內容都代理給了PHP,爲何?Apache也許用來替代,但不必。try_files指令神奇的地方是:它按必定的順序嘗試使用文件。NGINX能夠首先嚐試靜態文件,若是沒有,繼續嘗試。意思是PHP不必定要處理全部文件。速度將更快!例如你有個1M的圖片比較下經過用PHP來處理和用服務器直接處理的速度。看看優化後的方案:

server {
    server_name _;
    root /var/www/site;
    location / {
        try_files $uri $uri/ @proxy;
    }
    location @proxy {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/tmp/phpcgi.socket;
    }
}

一樣也是好的方案

server {
    server_name _;
    root /var/www/site;
    location / {
        try_files $uri $uri/ /index.php;
    }
    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/tmp/phpcgi.socket;
    }
}

很容易不是嗎?若是請求的URI存在,NGINX能夠直接處理,若是不存在,若是是個目錄也能夠處理,若是還不是,再傳遞給代理。僅當NGINX不能處理時才傳遞給代理。

考慮下你的多少內容是靜態的(圖片、css、javascript等),可能能節省下大量的消耗。

緩存問題

瀏覽器緩存的問題。你的配置也許很完善但也有撞牆的時候。出現問題的緣由就是瀏覽器緩存。當下載頁面時,瀏覽器會作緩存,同時也保存了文件怎麼更新,若是你在測試 types{}指令,你可能須要經過如下方法來清緩存

  1. 在firefox使用ctrl+shift+delete,清掉緩存;
  2. 使用curl查看文件是否更新

VirtualBox虛擬機

若是nginx工做不正常多是由於sendfile指令,只要註釋掉再試下。

丟失的HTTP頭信息

若是你沒有設置underscores_in_headers on;指令,NGINX將把包括下劃線的頭信息丟掉(依據http標準),這也是爲了一種保護機制,防止異常的頭信息與CGI變量衝突。

沒有使用標準的Document Root 位置信息

全部系統都有些不能使用的目錄,如/和root,永遠不要設置成document root目錄。通常都是放在/var/www或/srv或/usr/share/www/。

使用默認的Document Root

不要使用系統安裝包默認的DocumentRoot目錄,由於軟件升級或從新安裝時,有可能修改該目錄,若是你的業務恰好在使用這個目錄,oops,目錄有可能被刪除並安裝它的默認文件。當心。

使用主機名來解析地址

劣:

upstream {
    server http://someserver;
}

優:

server {
    listen myhostname:80;
    # [...]
}

永遠不要在listen指令中使用主機名。雖然能夠工做,但會帶來不少問題,一個是系統啓動或service啓動時hostname不必定能解析,這將致使nginx不能綁定到指令的tcp socket並啓動失敗。

安全的作法是指令ip地址來代替主機名。避免了域名解析的成本。同理,在upstream也同樣,儘可能避免使用主機名。

優:

upstream {
    server http://10.48.41.12;
}

注:多虛擬主機名時仍是避免不了。

HTTPS使用SSLv3

SSLv3由於有安全風險,建議不要使用,而用TLS代替。

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

翻譯 朱淦<350050183@qq.com> 2016.7.20

相關文章
相關標籤/搜索