分类: LINUX
2010-09-25 15:22:21
If you're new or old to Nginx you will likely wind up in a pitfall situation. Below we outline these pitfalls and how to best avoid them. These are issues seen time and time again in the #nginx channel on Freenode. Please, just don't do this.
Don't follow the guides out there unless you can understand what they're trying to do and can clean it up. So many of the configs out there are horribly written. The Pitfalls page was written entirely from poor configs found on the Internet. They weren't found by searching but were instead found by support requests asking why something doesn't work. The common response is to disagree with what we say because some random guide says to do it the wrong way. That's what this page is designed to address. If you're having issues, this is the place to look.
BAD:
{ / { /var/www/nginx-default/; [...] } /foo { /var/www/nginx-default/; [...] } /bar { /var/www/nginx-default/; [...] } }
This works. 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. Let's look at a good configuration.
GOOD:
{ /var/www/nginx-default/; / { [...] } /foo { [...] } /bar { [...] } }
BAD:
{ .php .htm .html; { / { .php .htm .html; [...] } } { domain.com; / { .php .htm .html; [...] } /foo { .php; [...] } } }
Why repeat so many lines when not needed. Simply use the "index" directive one time. It only needs to occur in your http { } block and it will be inherited below.
GOOD:
{ .php .htm .html; { / { [...] } } { domain.com; / { [...] } /foo { [...] } } }
BAD:
{ 80; [...] }
You don't need "listen 80;" It is implied by Nginx. The only time you ever need to add this is if your adding "listen 80 default;" which will make that the default server block if no others are hit. Save the line, just don't add it.
GOOD:
{ [...] }
There is a little page about using if statements. It's called and you really should check it out. Let's take a look at a few uses of if that are bad.
BAD:
{ domain.com *.domain.com; ($host ~* ^www\.(.+)) { $raw_domain $1; ^/(.*)$ $raw_domain/$1 permanent; [...] } }
There are actually three problems here. The first being the if. That's what we care about now. Why is this bad? Did you read ? When nginx receives a request no matter what is the subdomain being requested, be it or just the plain domain.com this if directive is always evaluated. Since you're requesting nginx to check for the Host header for every request. It's extremely inefficient. You should avoid it. Instead use two server directives like the example below.
GOOD:
{ ^ $scheme://domain.com$request_uri permanent; } { domain.com; [...] }
Besides making the configuration file easier to read. This approach decreases nginx processing requirements. We got rid of the spurious if. We're also using which doesn't hardcodes the URI scheme you're using, be it http or https.
Using if to ensure a file exists is horrible. It's mean. If you have any recent version of Nginx you should look at which just made life much easier.
BAD:
{ /var/www/domain.com; / { (!-f $request_filename) { ; } } }
GOOD:
{ /var/www/domain.com; / { $uri $uri/ .html; } }
What we changed is that we try to see if $uri exists without requiring an if. Using try_files mean 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 it will see if the $uri file exists. If it does then serve it. If it doesn't then tests if that directory exists. If not, then it will proceed to serve index.html which you make sure exists. It's loaded but oh so simple. This is another instance you can completely eliminate If.
"Front Controller Pattern" designs are popular and used on the many of the most popular PHP software packages. A lot of examples are more complex than they need to be. To get Drupal, Joomla, WordPress, etc. to work, just use this:
$uri $uri/ /.php?q=$request_uri;
Note - the parameter names are different based on the package you're using. For example:
Some software doesn't even need the query string, and can read from PATH_INFO (WordPress does support this, for example):
$uri $uri/ /.php;
Of course, your mileage may vary and you may need more complex things based on your needs, but for a basic sites, these will work perfectly. You should always start simple and build from there.
You can also decide to skip the directory check and remove "$uri/" from it as well, if you don't care about checking for the existence of directories.
It is common with Nginx to pass every URI ending in .php to the PHP parser, if using a default PHP build this might lead to security issues. Nginx is a reverse proxy and as such does not have a concept of file unless you specifically tell it to. So if your configuration looks like this.
~* \.php$ { backend; }
Then you are probably vulnerable. The issue is that PHP when configured incorrectly tries to guess which file you want to execute if the full path does not lead to a file. Say you have the URI /forum/avatar/1232.jpg/index.php. The File does not exist but /forum/avatar/1232.jpg does, Nginx does not care and gladly passes the request to PHP as you have instructed it to, PHP sees the file does not exist and that /forum/avatar/1232.jpg does and chooses to execute this. If this file is a user upload it might contain embedded PHP code and you are now vulnerable to arbitrary code execution.
The solution is to set cgi.fix_pathinfo=0 in the php.ini file, this causes PHP to try the literal path given and will thus not execute the jpg file. If for backwards compatibility reasons you cannot change this setting you need to ensure that Nginx is passing PHP an actual file or specifically disable PHP access to any directory containing user uploads.
So many guides out there like to rely on absolute paths to get to your information. This is commonly seen in PHP blocks. 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/.
The line usually looks like this:
SCRIPT_FILENAME $document_root$fastcgi_script_name; ##GOOD
Don't change it to something like this:
SCRIPT_FILENAME /var/www/yoursite.com/$fastcgi_script_name; ##BAD
Where is $document_root set? It's set by the root directive that should be in your server block. Is your root directive not there? See the .
Don't feel bad here, it's easy to get confused with regular expressions. In fact, it's so easy to do that we should make an effort to keep them neat and clean. Quite simply, don't add cruft.
BAD:
^/(.*)$ ://domain.com/$1 permanent;
GOOD:
^ ://domain.com$request_uri permanent;
Look at the above. Then back here. Then up, and back here. OK. The first rewrite captures the full URI minus the first slash. By using the built-in variable $request_uri we can effectively avoid doing any capturing or matching at all.
Very simply, rewrites are internal unless you tell Nginx that they're not. This is very simple. Add a protocol.
BAD:
^ domain.com permanent;
GOOD:
^ ://domain.com permanent;
In the above you will see that all we did was add "http://" to the rewrite. It's simple, easy, and effective.
BAD:
{ _; /var/www/site; / { fastcgi_params; SCRIPT_FILENAME $document_root$fastcgi_script_name; unix:/tmp/phpcgi.socket; } }
Yucky. In this instance, you pass EVERYTHING to PHP. Why? Apache might do this, you don't need to. Let me put it this way... The directive exists for an amazing reason. It tries files in a specific order. This means that Nginx can first try to server the static content. If it can't, then it moves on. This means PHP doesn't get involved at all. MUCH faster. Especially if you're serving a 1MB image over PHP a few thousand times versus serving it directly. Let's take a look at how to do that.
GOOD:
{ _; /var/www/site; / { $uri $uri/ @proxy; } @proxy { fastcgi_params; SCRIPT_FILENAME $document_root$fastcgi_script_name; unix:/tmp/phpcgi.socket; } }
It's easy, right? You see if the requested URI exists and can be served by Nginx. If not, is it a directory that can be served. If not, then you pass it to your proxy. Only when Nginx can't serve that requested URI directly does your proxy overhead get involved.
Now.. consider how much of your requests are static content, such as images, css, javascript, etc. That's probably a lot of overhead you just saved.
Browser cache. Your configuration may be perfect but you'll sit there and beat your head against a cement wall for a month. What's wrong is your browser cache. When you download something, your browser stores it. It also stores how that file was served. If you are playing with a types{} block you'll encounter this.
The fix:
[Option 1] In Firefox press Ctrl+Shift+Delete, check Cache, click
Clear Now. In any other browser just ask your favorite search engine. Do
this after every change (unless you know it's not needed) and you'll
save yourself a lot of headaches.
[Option 2] Use curl.