【背景】
在我实际使用的环境中很少直接使用 nginx + php-fpm 方式搭建环境,大部分还都是使用apache,即便用到nginx,还只是用它监听80端口再代理apache的php做负载均衡器。
这次偶尔机会自己搭建了 nginx + php-fpm 环境,发现自己开发的MyQEE输出js、css以及image图片时会出现异常关闭的问题。
nginx配置了类似这样的rewrite
rewrite ^/.* /index.php last;
}
rewrite的意思是当请求的实际文件不存在时rewrite到index.php上
【那么问题来了】
由于有那个rewrite的存在,所以当请求类似 http://127.0.0.1/test.js 的URL,如果服务器上没有test.js,此时就会重定向导index.php,并不会直接返回404错误,而是由index.php来决定。
然后由这个index.php根据一些参数(例如uri=test.js)载入了另外一个js,并模拟页面输出,输出了一个200的头信息。
代码类似如下:
这个看上去似乎没有任何问题,但是在nginx里却出现了奇怪问题。
浏览器再接受到部分内容输出后直接被关闭了。打开调试输出可以看到类似:
NET:ERR_CONTENT_LENGTH_MISMATCH
这样的错误。
如果在命令行里使用curl请求时,则会看到类似
这样的错误。
【问题分析】
首先来看 nginx 的配置,类似如下:
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
这个实际上是nginx通过代理php-fpm来实现的输出,当一个css、js、图片等正常请求因为rewrite的原因被rewrite到了php上,nginx内部会认为是一个文档处理,然后对文档进行压缩获得了压缩后的内容长度,在输出达到这个长度后就错误的关闭了tcp连接,但是返回的header头信息的长度却是压缩前的长度,这样就导致了之前的错误。
【问题解决】
我尝试过把gzip的功能关闭但是实际上没有启到任何作用,最后我是通过这样的方法解决的,在gzip的gzip_types参数把对应的文件都加上这样就解决问题了,代码如下:
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# 以下解决用php输出js,css等文件导致出错的问题
gzip on;
gzip_min_length 1100;
gzip_buffers 4 8k;
gzip_types text/plain application/x-javascript text/css image;
}
2015-09-25更新
本人另外一个服务器实测也遇到这个问题,需要在加上才可以
在nginx的默认配置里 gzip_types就只有 text/plain
【总结】
实际上的解决就是把对应输出的文件类型让nginx再压缩下即可,但是如果php输出的是图片或一些已经压缩过的二进制文件,那么这样做实际上是增加了服务器的负担。
所以如果有大量的图片、swf、zip等文件,建议还是通过sendfile的方式进行转发。
sendfile可参考: