Nginx 经验小结


chmod 777
永远不要  使用 777,有时候可以懒惰的解决权限问题, 但是它同样也表示你没有线索去解决权限问题,你只是在碰运气。 你应该检查整个路径的权限,并思考发生了什么事情。
把 root 放在 location 区块内
糟糕的配置:
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/; # [...] }}
这个是能工作的。把 root 放在 location 区块里面会工作,但并不是完全有效的。 错就错在只要你开始增加其他的 location 区块, 就需要给每一个 location 区块增加一个 root。 如果没有添加,就会没有 root。 让我们看下正确的配置。
推荐的配置:
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 指令的几个邪恶的用法。
注意看这里
用 if 判断 Server Name
糟糕的配置:
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 header,这是十分 低效的 。 你应该避免这种情况,而是使用下面配置里面的两个 server 指令。
推荐的配置:
server { server_name www.example.com; return 301 $scheme ://example.com $request_uri ;}server { server_name example.com; # [...] }
除了增强了配置的可读性,这种方法还降低了 Nginx 的处理要求;我们摆脱了不必要的 if 指令; 我们 用了 $scheme 来表示 URI 中是 http 还是 https 协议,避免了硬编码。
用 if 检查文件是否存在
使用 if 指令来判断文件是否存在是很可怕的,如果你在使用新版本的 Nginx, 你应该看看 try_files,这会让你的生活变得更轻松。
糟糕的配置:
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 来判断 $uri 是否存在,用 try_files 意味着你可以测试一个序列。 如果 $uri 不存在,就会尝试 $uri/,还不存在的话,在尝试一个回调 location。
在上面配置的例子里面 ,如果 $uri 这个文件存在,就正常服务; 如果不存在就检测 $uri/ 这个目录是否存在;如果不存在就按照 index.html 来处理 ,你需要保证 index.html 是存在的。 try_files 的加载是如此简单。


前端控制器模式的 web 应用
“前端控制器模式”是流行的设计,被用在很多非常流行的 PHP 软件包里面。 里面的很多示例配置都过于复杂。想要 Drupal, Joomla 等运行起来,只用这样做就可以了:
try_files $uri $uri / /index.php?q= $uri & $args ;
注意:你实际使用的软件包,在参数名字上会有差异。

一些软件甚至不需要查询字符串,它们可以从 REQUEST_URI 中读取。 比如 WordPress 就支持这样的配置:
try_files $uri $uri / /index.php;
当然在你的开发中可能会有变化,你可能需要基于你的需要设置更复杂的配置。 但是对于一个基础的网站来说,这个配置可以工作得很完美。 你应该永远从简单开始来搭建你的系统。
如果你不关心目录是否存在这个检测的话,你也可以决定忽略这个目录的检测,去掉 “$uri/” 这个配置。
把不可控制的请求发给 PHP
很多网络上面推荐的和 PHP 相关的 Nginx 配置,都是把每一个  .php  结尾的 URI 传递给 PHP 解释器。 请注意,大部分这样的 PHP 设置都有 严重的安全问题 ,因为它可能允许执行任意第三方代码。
有问题的配置通常如下:
location ~* \.php$ { fastcgi_pass backend; # [...] }
在这里, 每一个 .php 结尾的请求 ,都会传递给 FastCGI 的后台处理程序。 这样做的问题是,当完整的路径 未能指向 文件系统里面一个 确切的文件时 , 默认的 PHP 配置试图 猜测你想执行的是哪个文件。
举个例子,如果一个请求中的  /forum/avatar/1232.jpg/file.php  文件不存在, 但是  /forum/avatar/1232.jpg  存在,那么 PHP 解释器就会取而代之, 使用  /forum/avatar/1232.jpg  来解释。如果这里面嵌入了 PHP 代码, 这段代码就会被执行起来。
有几个避免这种情况的选择:
  • 在 php.ini 中设置 cgi.fix_pathinfo=0。 这会让 PHP 解释器只尝试给定的文件路径,如果没有找到这个文件就停止处理。
  • 确保 Nginx 只传递指定的 PHP 文件去执行
location ~* (file_a|file_b|file_c)\.php$ { fastcgi_pass backend; # [...] }
  • 对于任何用户可以上传的目录,特别的关闭 PHP 文件的执行权限
location /uploaddir { location ~ \.php$ { return 403 ;} # [...] }
  • 使用 try_files 指令过滤出文件不存在的情况
location ~* \.php$ { try_files $uri = 404 ; fastcgi_pass backend; # [...] }
  • 使用嵌套的 location 过滤出文件不存在的情况
location ~* \.php$ { location ~ \..*/.*\.php$ { return 404 ;} fastcgi_pass backend; # [...] }

脚本文件名里面的 FastCGI 路径
很多外部指南喜欢依赖绝对路径来获取你的信息。这在 PHP 的配置块里面很常见。 当你从仓库安装 Nginx,通常都是以在配置里面折腾好“ include fastcgi_params; ”来收尾。 这个配置文件位于你的 Nginx 根目录下,通常在  /etc/Nginx/  里面。
推荐的配置:
fastcgi_param SCRIPT_FILENAME $document_root$ fastcgi_script_name ;
糟糕的配置:
fastcgi_param SCRIPT_FILENAME /var/www/yoursite.com/ $fastcgi_script_name ;
$document_root$ 在哪里设置呢?它是 server 块里面的 root 指令来设置的。 你的 root 指令不在 server 块内?请看前面关于 root 指令的陷阱。

费力的 rewrites
不要知难而退,rewrite 很容易和正则表达式混为一谈。 实际上,rewrite 是很容易的,我们应该努力去保持它们的整洁。 很简单,不添加冗余代码就行了。
糟糕的配置:
rewrite ^/(.*)$ http://example.com/ $1 permanent ;
好点儿的配置:
rewrite ^ http://example.com $request_uri ? permanent ;
更好的配置:
return 301 http://example.com $request_uri ;
反复对比下这几个配置。 第一个 rewrite 捕获不包含第一个斜杠的完整 URI。 使用内置的变量 $request_uri,我们可以有效的完全避免任何捕获和匹配。
忽略 http:// 的 rewrite
这个非常简单,rewrites 是用相对路径的,除非你告诉 Nginx 不是相对路径。 生成绝对路径的 rewrite 也很简单,加上 scheme 就行了。
糟糕的配置:
rewrite ^ example.com permanent ;
推荐的配置:
rewrite ^ http://example.com permanent ;
你可以看到我们做的只是在 rewrite 里面增加了  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 可能要这样做,但在 Nginx 里你不必这样。 换个思路,try_files 有一个神奇之处,它是按照特定顺序去尝试文件的。 这意味着 Nginx 可以先尝试下静态文件,如果没有才继续往后走。 这样 PHP 就不用参与到这个处理中,会快很多 。 特别是如果你提供一个 1MB 图片数千次请求的服务,通过 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 处理; 只有在 Nginx 不能直接处理请求的 URI 的时候,才会进入 proxy 这个 location 来处理。
现在,考虑下你的请求中有多少静态内容,比如图片、css、javascript 等。这可能会帮你节省很多开销。
配置的修改没有起效
浏览器缓存。
VirtualBox
如果你在 VirtualBox 的虚拟机中运行 Nginx,而它不工作,可能是因为 sendfile() 引起的麻烦。 只用简单的注释掉 sendfile 指令,或者设置为 off。该指令大都会写在 Nginx .conf 文件中:
sendfile off;

丢失(消失)的 HTTP 头
如果你没有明确的设置 underscores_in_headers on; , Nginx 将会自动丢弃 带有下划线的 HTTP 头 (根据 HTTP 标准,这样做是完全正当的). 这样做是为了防止头信息映射到 CGI 变量时产生歧义,因为破折号和下划线都会被映射为下划线。
没有使用标准的 Document Root Location
在所有的文件系统中,一些目录永远也不应该被用做数据的托管。这些目录包括 / 和 /root。 你永远不应该使用这些目录作为你的 document root。
使用这些目录的话,等于打开了潘多拉魔盒,请求会超出你的预期获取到隐私的数据。
永远也不要这样做!!!  ( 对,我们还是要看下飞蛾扑火的配置长什么样子)
server { root /; location / { try_files /web/$uri $uri @php; } location @php { [...] }}
当一个对 /foo 的请求,会传递给 PHP 处理,因为文件没有找到。 这可能没有问题,直到遇到 /etc/passwd 这个请求 。没错,你刚才给了我们这台服务器的所有用户列表。 在某些情况下,Nginx 的 workers 甚至是 root 用户运行的。那么,我们现在有你的用户列表, 以及密码哈希值,我们也知道哈希的方法。这台服务器已经变成我们的肉鸡了。
Filesystem Hierarchy Standard (FHS) 定义了数据应该如何存在。你一定要去阅读下。 简单点儿说,你应该把 web 的内容  放在 /var/www/ , /srv 或者 /usr/share/www 里面
使用默认的 Document Root
在 Ubuntu、Debian 等操作系统中,Nginx 会被封装成一个易于安装的包, 里面通常会提供一个“默认”的配置文件作为范例,也通常包含一个 document root 来保存基础的 HTML 文件。
大部分这些打包系统,并没有检查默认的 document root 里面的文件是否修改或者存在。 在包升级的时候,可能会导致代码失效。有经验的系统管理员都知道,不要假设默认的 document root 里面的数据在升级的时候会原封不动。
你不应该使用默认的 document root 做网站的任何关键文件的目录。 并没有默认的 document root 目录会保持不变这样的约定,你 网站的关键数据, 很可能在更新和升级系统提供的 Nginx 包时丢失。
使用主机名来解析地址
糟糕的配置:
upstream { server http://someserver;}server { listen myhostname: 80 ; # [...] }
你不应该在 listen 指令里面使用主机名 。 虽然这样可能是有效的,但它会带来层出不穷的问题。 其中一个问题是,这个主机名在启 动时或者服务重启中不能解析 。 这会导致 Nginx 不能绑定所需的 TCP socket 而启动失败。
一个更安全的做法是使用主机名对应 IP 地址,而不是主机名。 这可以防止 Nginx 去查找 IP 地址,也去掉了去内部、外部解析程序的依赖。
例子中的 upstream location 也有同样的问题,虽然有时候在 upstream 里面不可避免要使用到主机名, 但这是一个不好的实践,需要仔细考虑以防出现问题。
推荐的配置:
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;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值