落とし穴とよくある間違い

ユーザの新しい古いに関係なく落とし穴に陥り得ます。以下で、頻繁に見かける問題の概要を説明し、それらの問題をどうやって解決するかを説明します。Freenode上の #nginx IRC チャンネルの中で、これらの問題を頻繁に見かけます。

このガイドで言いたいこと

The most frequent issue we see happens when someone attempts to just copy and paste a configuration snippet from some other guide. 表にある全てのガイドが間違っているわけでは無いですが、それらのうちの恐ろしい数がそうです。Linodeライブラリでさえ情報の品質が乏しく、幾つかのNGINXコミュニティのメンバーは訂正するために無駄に試みています。

これらのドキュメントは全ての種類のNGINXのユーザと直接仕事をするコミュニティのメンバーによって作成およびレビューされました。This specific document exists only because of the volume of common and recurring issues that community members see.

私の問題がリストされていない

あなたの特定の問題に関係する何かをここでYou don’t see something in here related to your specific issue. Maybe we didn’t point you here because of the exact issue you’re experiencing. Don’t skim this page and assume you were sent here for no reason. ここにリストされている何かの間違いをしたので、あなたはここに送られました。

When it comes to supporting many users on many issues, community members don’t want to support broken configurations. 助けを求める前に設定を修正してください。これを読んで設定を修正してください。ざっと読むだけにしないでください。

Chmod 777

NEVER use 777. It might be one nifty number, but even in testing it’s a sign of having no clue what you’re doing. 全てのパスのパーミッションを見て、何が起きているかを考えてください。

To easily display all the permissions on a path, you can use:

namei -om /path/to/check

locationブロック内のroot

BAD:

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

これは動作します。Putting root inside of a location block will work and it’s perfectly valid. What’s wrong is when you start adding location blocks. If you add a root to every location block then a location block that isn’t matched will have no root. Therefore, it is important that a root directive occur prior to your location blocks, which can then override this directive if they need to. 良い瀬手地を見てみましょう。

GOOD:

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

複数のindexディレクティブ

BAD:

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;
            # [...]
        }
    }
}

なぜ必要の無い時にあまりに多くの行を繰り返すのか。Simply use the index directive one time. It only needs to occur in your http{} block and it will be inherited below.

GOOD:

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

Using if

There is a little page about using if statements. それは Ifは邪悪 と呼ばれ、本当にそれを調べるべきです。Let’s take a look at a few uses of if that are bad.

参照

If は邪悪

Server Name (If)

BAD:

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

これには実際のところ3つの問題があります。The first being the if. 私達が今心配しているのはそれです。なぜこれが悪いのか?Ifは邪悪 を読みましたか?When NGINX receives a request no matter what is the subdomain being requested, be it www.example.com or just the plain example.com this if directive is always evaluated. Since you’re requesting NGINX to check for the Host header for every request. それは極度に非効率です。それはさけなければなりません。Instead use two server directives like the example below.

GOOD:

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

Besides making the configuration file easier to read. このやり方はNGINXの処理要求を減らします。We got rid of the spurious if. We’re also using $scheme which doesn’t hardcodes the URI scheme you’re using, be it http or https.

(もし) ファイルが存在したらを調べている

Using if to ensure a file exists is horrible. それは意地悪です。If you have any recent version of NGINX you should look at try_files which just made life much easier.

BAD:

server {
    root /var/www/example.com;
    location / {
        if (!-f $request_filename) {
            break;
        }
    }
}

GOOD:

server {
    root /var/www/example.com;
    location / {
        try_files $uri $uri/ /index.html;
    }
}

What we changed is that we try to see if $uri exists without requiring if. Using try_files means that you can test a sequence. If $uri doesn’t exist, try $uri/, if that doesn’t exist try a fallback location.

In this case, if the $uri file exists, serve it. If not, check if that directory exists. If not, then proceed to serve index.html which you make sure exists. It’s loaded–but oh-so-simple! This is another instance where you can completely eliminate If.

フロント コントローラーパターン Webアプリケーション

“Front Controller Pattern” designs are popular and are used on the many of the most popular PHP software packages; But a lot of examples are more complex than they need to be. For Drupal, Joomla, etc., just use this:

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

注意 - パラメータ名は使用するパッケージに基づいて異なります。例えば:

  • "q"はDrupal, Joomla, WordPressで使われるパラメータです。
  • "page"はCMS Made Simpleで使われます。

Some software don’t even need the query string and can read from REQUEST_URI. For example, WordPress supports this:

try_files $uri $uri/ /index.php;

If you don’t care about checking for the existence of directories, you can skip it by removing $uri/.

Of course, your mileage may vary and you may require something more complex based on your needs, but for basic sites, these will work perfectly. 常に簡単なものから始め、そこから構築するべきです。

制御できないリクエストをPHPに渡す

Many example NGINX configurations for PHP on the web advocate passing every URI ending in .php to the PHP interpreter. これは、サードパーティによる任意のコードの実行を許可するかも知れないので、ほとんどのPHPセットアップ上で深刻なセキュリティ問題を与えることに注意してください。

問題のセクションは通常このようなものです:

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

Here, every request ending in .php will be passed to the FastCGI backend. The issue with this is that the default PHP configuration tries to guess which file you want to execute if the full path does not lead to an actual file on the filesystem.

例えば、もし/forum/avatar/1232.jpg/file.phpへのリクエストが作られ、これが存在しないが /forum/avatar/1232.jpgが存在する場合、PHPインタプリタは代わりに /forum/avatar/1232.jpg を処理しようとするでしょう。もしこれが埋め込みのPHPコードを含む場合、コードはその結果実行されるでしょう。

これを避けるためのオプションは:

  • Set cgi.fix_pathinfo=0 in php.ini. これはPHPインタプリタに渡された文字通りのパスだけを試すようにし、ファイルが存在しなければ処理を停止させます。
  • NGINXは実行のための特定のPHPファイルだけを通すようにします:
location ~* (file_a|file_b|file_c)\.php$ {
    fastcgi_pass backend;
    # [...]
}
  • 特に、ユーザのアップロードを含む任意のディレクトリ内のPHPファイルの実行を無効にします:
location /uploaddir {
    location ~ \.php$ {return 403;}
    # [...]
}
  • Use the try_files directive to filter out the problem condition:
location ~* \.php$ {
    try_files $uri =404;
    fastcgi_pass backend;
    # [...]
}
  • 問題の条件を濾しとるために入れ子のlocationを使ってください:
location ~* \.php$ {
    location ~ \..*/.*\.php$ {return 404;}
    fastcgi_pass backend;
    # [...]
}

Scriptファイル名内のFastCGIパス

巷にあるあまりにも多くのガイドが情報を取得するために絶対パスに依存しがちです。これは一般的にPHPブロックに見受けられます。When you install NGINX from a repository, you’ll usually wind up being able to toss include fastcgi_params; in your config. This is a file located in your NGINX root directory which is usually around /etc/nginx/.

GOOD:

fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;

BAD:

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

Where is $document_root set?serverブロック内にある筈のrootディレクティブによって設定されます。rootディレクティブがありませんか?最初の落とし穴を見てください。

厄介なrewrite

気を悪くしないでください。正規表現を使って混乱するのは簡単です。In fact, it’s so easy to do that we should make an effort to keep them neat and clean. 完全に簡単に、酷いものを追加しないでください。

BAD:

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

GOOD:

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

BETTER:

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

上を見てください。そしてここに戻ってください。そして上に行き、ここに戻ってください。OK. 最初のrewriteは完全なURIから最初のスラッシュを引いたものをキャプチャします。By using the built-in variable $request_uri we can effectively avoid doing any capturing or matching at all.

Rewrite Missing http://

Very simply, rewrites are relative unless you tell NGINX that they’re not. Making a rewrite absolute is simple. スキーマを追加します。

BAD:

rewrite ^ example.com permanent;

GOOD:

rewrite ^ http://example.com permanent;

上で私達がしたことはrewriteにhttp:// を追加したことだと分かるでしょう。単純で、簡単で、効果的です。

全てをプロキシする

BAD:

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;
    }
}

Yucky. この例では、全てをPHPに渡しています。なぜ?Apache might do this, but you don’t need to. The try_files directive exists for an amazing reason: It tries files in a specific order. NGINX can first try to serve the static content, and if it can’t, it moves on. このことは、PHPは全く巻き込まれないことを意味します。とても速いです。1MBのイメージをphpを通じて数千回提供している場合に対して、直接それを提供する場合は特にそうです。それをどうやってやるかを見てみましょう。

GOOD:

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;
    }
}

Also GOOD:

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;
    }
}

Easy, right?See if the requested URI exists and can be served by NGINX. If not, see if it is a directory that can be served. If not, then pass it to your proxy. NGINXがリクエストされたURIを直接提供できない場合のみ、プロキシのオーバーヘッドが巻き込まれます。

Consider how much of your requests are for static content (images, css, javascript, etc.). おそらく大量のオーバーヘッドが節約されます。

設定の変更が反映されない

ブラウザのキャッシュあなたの設定は完璧かも知れませんが、一ヶ月の間そこに座って頭を記憶の壁に打ち付けているでしょう。間違っているのはあなたのブラウザのキャッシュです。何かをダウンロードした時に、ブラウザはそれを保存します。ブラウザはファイルがどうやって提供されたかも保存します。If you are playing with a types{} block you’ll encounter this.

処置:

  • Firefoxでは、Ctrl+Shift+Delete を押し、Cacheをチェックし、Clear Nowをクリックします。In any other browser, just ask your favorite search engine. (必要ないと知らない場合は)各変更の後にこれをしてください。自身を多くの頭痛から助けるでしょう。
  • curlを使ってください。

VirtualBox

これが動作せず、NGINXをVirtualBox内のvirtulaマシーン上で実行している場合は、問題を起こしているのはsendfile()かも知れません。単純にsendfileディレクティブをコメントアウトするか、それを"off"に設定します。ディレクティブはほとんどnginx.confファイルの中で見つかります:

sendfile off;

失われた (消えた) HTTP ヘッダ

If you do not explicitly set underscores_in_headers on;, NGINX will silently drop HTTP headers with underscores (which are perfectly valid according to the HTTP standard). これはダッシュおよびアンダースコアの両方が処理の間にアンダースコアにマップされるために、ヘッダーがCGIにマッピングされる時の曖昧さを避けるために行われます。

標準のドキュメントルートのlocationを使わないでください

どのようなファイルシステムにもデータをホストするために使われない幾つかのディレクトリがあります。These include / and root. それらをドキュメントルートとして使うべきではありません。

Doing this leaves you open to a request outside of your expected area returning private data.

これをしないでください!!!(yes, we have seen this)

server {
    root /;

    location / {
        try_files /web/$uri $uri @php;
    }

    location @php {
        # [...]
    }
}

When a request is made for /foo, the request is passed to php because the file isn’t found. This can appear fine, until a request in made for /etc/passwd. はい。そのサーバ上の全てのユーザのリストを渡しました。幾つかの場合で、NGINXサーバはrootとしてワーカーを実行するようにセットアップされさえします。Yup, we now have your user list as well as password hashes and how they’ve been hashed. これで私達はあなたの箱を所有します。

ファイルシステム階層標準はデータがどこにあるべきかを定義します。確実にそれを読むべきです。The short version is that you want your web content to exist in either /var/www/, /srv, /usr/share/www.

デフォルトのドキュメントルートを使う

NGINX packages that exist in Ubuntu, Debian, or other operating systems, as an easy-to-install package will often provide a ‘default’ configuration file as an example of configuration methods, and will often include a document root to hold a basic HTML file.

これらのパッケージングシステムは、デフォルトのドキュメントルート内でファイルが修正されたかあるいは存在するかどうかを調べないため、パッケージがアップグレードされた時にコードの紛失に繋がります。熟練したシステム管理者はデフォルトのドキュメントルート内のデータがアップグレードの間に触られないでいることを期待できない事を知っています。

サイトにとって致命的なファイルのためにデフォルトのドキュメントルートを使ってはいけません。デフォルトのドキュメントルートがシステムによって触られないでいるだろうという期待はできません。オペレーティングシステムのためにNGINXをアップデートおよびアップグレードする時にサイトにとって致命的なデータが失われえるかも知れない可能性はとても高いです。

アドレスの解決のためにホスト名を使う

BAD:

upstream {
    server http://someserver;
}

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

listenディレクティブの中でホスト名を使うべきではありません。これは動作するかも知れませんが、ものすごく多くの問題が付いてきます。そのような問題の一つは、ホスト名は起動時あるいはサービスの再起動の間に解決されないかも知れないということです。これはNGINXが希望するTCPソケットにバイドできないようにするかも知れず、それはNGINXを全く起動できなくするでしょう。

安全な慣習は、ホスト名の変わりに結合されるべきIPアドレスを知り、そのアドレスを使うことです。これにより、NGINXはアドレスのルックアップをしなくて済み、外部および内部のリゾルバへの依存を取り除きます。

これと同じ問題はupstream locationにも適用されます。upstreamブロック内でホスト名を使うことを常に避けることはできないかも知れませんが、それは悪い慣習であり、問題を避けるために注意深い考慮を要求するでしょう。

GOOD:

upstream {
    server http://10.48.41.12;
}

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

HTTPSと一緒にSSLv3を使う

SSLv3のPOODLE 脆弱性のため、SSLを有効にしたサイトではSSLv3を使わないことをお勧めします。この行を使ってSSLv3をとても簡単に無効にすることができ、代わりにTLSプロトコルだけを提供します:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
TOP
inserted by FC2 system