nginx、php-fpm、php 三者的配置文件中都有 error_log 项,指定各自错误日志的保存路径。理论上它们三者的错误应该不会重合,即 nginx error_log 记录的是 nginx 进程自己的错误,php-fpm error_log 记录的是 php-fpm 进程自己的错误, php error_log 记录的是 php 脚本执行时候的错误。 用户访问 nginx 服务,nginx 将用户请求转发给 php-fpm,然后由 php-fpm 调用 php 解析器来执行 php 脚本文件。在这个过程中我们最关心的是执行 php 脚本文件时候产生的 php 错误。 默认配置 通常,默认的配置文件如下所示: nginx.conf http { error_log /var/log/nginx/error.log warn; } 通常,我们还会在 nginx.conf 中指定不同 server 段自己的 error_log,这样就可以把不同 server 的错误日志分开到不同的日志文件中。 php-fpm.conf (/etc/php-fpm.d/www.conf) error_log = /var/log/php-fpm/error.log ;catch_workers_output = yes php_admin_value[error_log] = /var/log/php-fpm/www-error.log php_admin_flag[log_errors] = on php-fpm 的 error_log 正常情况下只会记录 php-fpm 进程自己的信息,不会记录 php 脚本的执行错误。 后面三个配置项通常是放在 /etc/php-fpm.d/www.conf 中。 catch_workers_output 表示是否将每个 php-fpm worker 的 stdout 和 stderr 输出重定向到 error_log 中,默认不开启。而 php-fpm 的每个 worker 实际上就可以看作是 php 解析器。 php_admin_value[error_log] 会覆盖 php.ini 中的 error_log 配置。 php_admin_flag[log_errors] 会覆盖 php.ini 中的 log_errors 配置。 之所以会需要在 php-fpm.conf 覆盖 php.ini 中的配置项,是因为 php.ini 定义了整个 php 环境的配置,这就包括了在命令行直接执行 php script.php 时候的配置。而有些配置我们并不想影响全局,只想在 php-fpm 调用时起作用。 php.ini log_errors = On ;error_log = php_errors.log ;error_log = syslog log_errors 表示是否输出错误信息,如果关闭的话,连 stderr 都不会输出。 error_log 表示将错误信息输出到何处。可以指定日志文件的具体路径,指定为 syslog 的话则表示输出到系统日志中。如果不设置该值,则表示将错误信息输出到 stderr。php.ini 默认不设置该值。 所以根据上面的配置,/var/log/nginx/error.log,/var/log/php-fpm/error.log,/var/log/php-fpm/www-error.log 分别记录的是 nginx,php-fpm,php 脚本的错误信息。 php 日志读写权限 有时候即使确认了配置如上述一样,却无法在 /var/log/php-fpm/ 目录下找到 www-error.log 文件,而与此同时却在 /var/log/nginx/error.log 文件中发现了 php 脚本执行时错误信息。比如这样: 这很可能是因为 php 没有 /var/log/php-fpm/ 目录的读写权限,无法写入 /var/log/php-fpm/www-error.log。这时它就会将错误信息输出到 php-fpm worker 进程的 stderr,而 nginx 就会将它从 php-fpm 的 stderr 收到的任何信息以 error 等级写入自己的错误日志。 php-fpm master 进程通常是 root 用户,而 php-fpm 执行 php 脚本所使用的用户(亦即 php-fpm worker 进程的用户)是定义在 /etc/php-fpm.d/www.conf 文件中。 user = www group = www 所以只需要确保 php-fpm worker 有读写 /var/log/php-fpm/ 目录的权限,php 错误信息就能正确写入 /var/log/php-fpm/www-error.log 文件中。而 nginx 的 error_log 也就不会再收到 php 错误信息。 但是其实这种误打误撞的结果倒是我想要的。因为有时候我们会在 nginx 服务上部署多个网站,每个网站都通过 server 段中的 error_log 配置指定了自己的错误日志路径。所以我会更希望将 php 错误信息输出到对应网站的错误日志中,而不是统一输出到 php 的 error_log 中。 将 php 错误信息写到 nginx 站点的 error_log 不像上面那种 www-error.log 无写权限的歪路子,为了正确的将 php 错误信息写入到 nginx 每个站点各自的 error_log 文件中,需要保证如下几点: 在 /etc/php-fpm.d/www.conf 中开启 php_admin_flag[log_errors] = on。 在 /etc/php-fpm.d/www.conf 中注释掉 php_admin_value[error_log] 配置(并确保 php.ini 中也没有设置 error_log),让 php 的错误信息输出到 php-fpm worker 进程的 stderr。 在 nginx…
提高https载入速度,记一次nginx升级优化
1. 发现问题 两年前就把我的 hawu.me 开启了 https,用的 Let's Encrypt 的免费证书。但因为只是自用,而且由于墙的原因,从来没有留意过加载速度慢的问题。今天特意观察了一下,初次打开网站居然要耗时4、5秒,这还不包括加载资源的时间。 使用 Chrome 的开发者工具看了下耗时: 在初次建立连接的时候,Initial connection 与 SSL 时间居然都用到了4秒 (\"▔□▔)汗。Chrome 开发者工具这里的 Initial connection 时间应该是包含了 SSL 时间(即 SSL 握手时间),如果没有 SSL 的话,Initial connection 应该就是指普通的 TCP 三次握手。 https 连接建立时间包括:tcp 连接建立时间 + tls 连接建立时间。参考 为什么 HTTPS 需要 7 次握手以及 9 倍时延。 连接建立之后的后续访问,由于不需要再进行握手,所以快了很多: 但这个初次建立连接的时间确实太长了,如何才能减少这个握手时间呢。 2. 升级 nginx 检查了一下服务器的 nginx 版本,发现是 1.12 的,现在的最新版本应该是 1.18 了。也没考虑太多,顺手就是一个 yum upgrade,然后就把 nginx 升级到版本 1.16.1。再进行一次测试,发现 https 握手时间只需要 350ms 左右了,以为这样差不多稳了。 但是第二天再测试时候,却发现一个奇怪的现象。如果重启浏览器后的初次访问,https 的握手时间还是很耗时,依然需要3到4秒。之后如果不重启浏览器,而是重启 nginx 服务,然后再次访问,此时浏览器与服务器之间的 tcp 连接应该已经中断了,需要重新握手。但这个第二次的握手却还是挺快的,只需要大概 350ms,跟前一天测试的一样。 由于并不了解https握手的详细步骤,我还在猜想是不是由于客户端浏览器没有重启,有些状态是还保留在客户端的,所以再次握手的时候可以忽略客户端某些步骤,所以第二次握手会快一些。 后来在网络上搜索的时候,发现这一篇帖子《部署 Let's Encrypt 的站点国内首次打开很慢?那是因为 OSCP 域名被墙了》。另外根据《HTTPs入门, 图解SSL从回车到握手》这篇文章的解释,握手中有一个阶段是客户端浏览器需要同步向证书颁发者验证证书吊销状态。如果Let's Encrypt被墙的话,握手就会卡在这里。而后面再次握手的话,我猜由于浏览器没有重启,还记得这个证书的吊销状态,所以就跟正常情况是一样快的。 So,我尝试将 letsencrypt.org 域名加入翻墙软件的 pac 列表,然后多次重启浏览器进行连接测试。发现这时的 Initial connection 耗时都在 400ms 左右。即使有时候由于梯子的网络原因,会超过 1s,但怎么样也不会出现耗时 3s+ 的情况了。 所以。。。会出现这种https握手超过 3s 的情况,真他妈是墙的原因啊!连证书服务器都墙,简直丧心病狂。凸(-。-; 所以。。。我原来升级前的 nginx 1.12,应该并不是它导致的 https 连接慢,而是由于墙啊!算了,也亏了它,我才会去研究这些 https 提速的问题,才会写下这篇文章。我谢谢你啊 GFW 🙏 3. 再升级 nginx,开启 TLSv1.3 上面通过 yum 直接升级到的 nginx 版本信息如下: nginx version: nginx/1.16.1 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) built with OpenSSL 1.0.2k-fips 26 Jan 2017 TLS SNI support enabled 它是不支持 tlsv1.3 的。要想在 centos 7 中安装支持 tlsv1.3 的nginx,只能手工对 nginx 源码进行编译安装。网上有很多教程,我是参考的 这个脚本 进行手工安装的。 升级到的版本为: nginx version: nginx/1.18.0 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) built with OpenSSL 1.1.1g 21 Apr 2020 TLS SNI support enabled 需要注意的一点是,在 /lib/systemd/system/nginx.service 文件中,建议删除 PIDFile=/run/nginx.pid 这一行。否则你会在 systemctl status nginx 输出中看到这么一个报错信息:Failed to read PID from file /run/nginx.pid: Invalid argument,不过这条错误并不会影响 nginx 服务的启动,所以非强迫症患者可以不管它。 4. 测试工具 4.1 Chrome 开发者工具 通常,我们可以使用浏览器提供的开发者工具来进行网页载入时间的测试。比如上面那几张截图,都是来自 chrome 浏览器的开发者工具 — Network 面板 — Timing 标签页。 Queuing A request being queued indicates that: The request was postponed by the rendering engine because it's considered lower priority than critical resources (such as scripts/styles). This often happens with images. The request was put on hold to wait for an unavailable TCP socket that's about to free…
wnmp启停脚本
windows下启动mysql、php、nginx ::关闭回显 @echo off set WNMP_HOME=C:/wnmp set NGINX_HOME=C:/wnmp/nginx-1.14.0 set PHP_HOME=C:/wnmp/php-7.2.7-nts-Win32-VC15-x64 echo Starting mysql... set status=1 (tasklist|find /I "mysqld.exe" || set status=0) 2>nul 1>nul if %status% equ 1 ( echo mysqld.exe already exists ) else ( ::net start 命令必须用管理员权限才能运行 net start mysql ) ::输出换行 echo. echo Starting php-cgi... set status=1 (tasklist|find /I "php-cgi.exe" || set status=0) 2>nul 1>nul if %status% equ 1 ( echo php-cgi.exe already exists ) else ( %WNMP_HOME%/RunHiddenConsole.exe %PHP_HOME%/php-cgi.exe -b 127.0.0.1:9000 -c %PHP_HOME%/php.ini echo php-cgi.exe start! ) ::输出换行 echo. echo Starting nginx... set status=1 (tasklist|find /I "nginx.exe" || set status=0) 2>nul 1>nul if %status% equ 1 ( echo nginx.exe already exists ) else ( %WNMP_HOME%/RunHiddenConsole.exe %NGINX_HOME%/nginx.exe -p %NGINX_HOME% echo nginx.exe start! ) ::输出换行 echo. pause 关闭nginx、php、mysql ::关闭回显 @echo off echo Stopping nginx... taskkill /F /IM nginx.exe > nul echo. echo Stopping php-cgi... taskkill /F /IM php-cgi.exe > nul echo. echo Stopping mysql... net stop mysql echo. pause 由于需要用到管理员权限运行,但原bat脚本无法设置每次默认用管理员权限运行, 只能点每次点右键进行管理员运行,这很蛋疼。 有个曲线救国的方法。把bat脚本放到其他目录下,然后发送快捷方式到桌面,右键快捷方式点击“属性”—“快捷方式”选项卡—“高级”—“用管理员身份运行”勾选上即可。
真 · nginx配置php文件解析(PATH_INFO支持与index.php隐藏)
nginx默认配置文件中对php的支持是这样的: # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # html改成项目路径 # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} nginx对php的处理原则是将匹配到的以.php结尾的文件转交给FastCGI服务进程(FastCGI是一种协议,php-fpm是实现了FastCGI协议的程序)。 但只启用这个配置是不够的,首先,他无法处理PATH_INFO信息。即在php文件中使用全局变量$_SERVER['PATH_INFO']只能得到空值。另外,他只能处理http://domain/index.php/controller/action?query_string这样的url,无法隐藏index.php。对于现在的web框架来说,将index.php文件作为php的单一入口,并在url上隐藏index.php是最基本的要求。有些框架还可能需要用到PATH_INFO来实现路由转发。 网上有好多资料讲怎么配置nginx来实现PATH_INFO支持与index.php隐藏的,但试了好多都是不完善的,要么支持了$_SERVER['PATH_INFO']但不支持隐藏index.php,要么是无法获取$_SERVER['QUERY_STRING']。就连我去年一直用到现在的配置其实也是有问题的。 今天试了好久,总算确定了一个最合理的配置: server { listen 80; server_name your_domain; location / { root your_code_path; index index.html index.htm index.php; # 无法找到的文件路径交给index.php处理 # $args变量表示查询字符串,将其传递给index.php,否则index.php无法获取QUERY_STRING try_files $uri $uri/ /index.php$is_args$args; } # 匹配.php文件 location ~ [^/]\.php(/|$) { # 从请求路径中匹配出$fastcgi_script_name与$fastcgi_path_info两个变量 fastcgi_split_path_info ^(.+?\.php)(/.*)$; # 如果.php文件不存在,返回404 if (!-f $document_root$fastcgi_script_name) { return 404; } root your_code_path; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } } 当然你也可以将后面两句fastcig_param写到/etc/nginx/fastcig_params文件中。 参考: https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/ @2020.05 DigitalOcean 出了一个可视化的 nginx 配置工具 nginxconfig.io,还是很好用的。 参考:https://www.hawu.me/operation/2129#5_nginx
nginx + tomcat配置
nginx使用反向代理将url转移给tomcat的8080端口。 server { listen 80; server_name www.domain.com; location / { root html; index index.html index.htm; proxy_pass http://localhost:8080/; # 下面的配置将用户访问的url传递给tomcat, # 否则传递给tomcat的只是localhost:8080了 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } 注意:由于使用了nginx做反向代理,所以在代码中request.getRemoteAddr()得到的不再是真实的用户ip,而是nginx服务器的ip(本机的话即为127.0.0.1)。参考这篇文章http://www.iteye.com/topic/1124492
nginx配置根域名重定向到www.域名
现在用户在输入url访问网站的时候通常不会再去敲http://www这样的前缀,而习惯直接敲域名。http会由浏览器自动添加,但www就必须由站长们来配置了。 首先需要在dns解析上配置对根域名的域名解析: 然后修改nginx的配置: # 301 redirect non-www to www server{ server_name hawu.me; return 301 $scheme://www.$host$request_uri; # return 301 等效于下面这句 # rewrite ^/(.*)$ http://www.hawu.me/$1 permanent; } server { listen 80; server_name www.hawu.me; # # 省略... # } 使用301重定向是对搜索引擎最友好的方式。
nginx访问不存在的php页面,“Primary script unknown”
最近看到nginx的error日志里总有这样的错误: 2015/03/16 17:17:42 [error] 27690#0: *1 FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream, client: 96.126.103.244, server: www.hawu.me, request: "GET /heihei.php HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "www.hawu.me" 原因是按照我先前的nginx配置,nginx总是将.php文件的访问一股脑交给php-fpm,当php-fpm访问不存在的例如这个heihei.php文件时候,就会返回404 file not found页面,并返回错误给nginx。 最好的解决方法,就是在 location ~ \.php$ {} 中添加一句 try_files $uri =404; location ~ \.php$ { root /www/hawu; try_files $uri =404; 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文件时候,找到的文件就交给php-fpm处理,找不到的文件就直接返回404。
nginx为网页开启gzip压缩
今天准备写汤圆网的app后台接口时,观摩了一下api.huaban.com的返回。发现人家的http响应头中有个值: Content-Encoding:"gzip" 看起来略屌对不对,我才知道原来http响应可以在先服务器端进行gzip压缩,然后传输,最后由浏览器自动解压!这样既能加速传输效率、又降低带宽占用。对于我这种带宽渣渣的小个体户,简直就是天降福音。 (sorry,以上都是废话) nginx关于gzip压缩模块的官方配置文档:http://nginx.org/en/docs/http/ngx_http_gzip_module.html 1. 打开nginx的配置文件 nginx.conf 2. 在http花括号里可以看到gzip功能被注释掉了: #gzip on; 3. 取消注释,开启gzip压缩,并添加如下配置: gzip on; gzip_comp_level 3; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/javascript application/x-javascript application/json; gzip_disable "MSIE [4-6]\."; gzip_vary on; gzip_comp_level: gzip压缩等级,从1~9可选,数值越大压缩比率越高,也越耗cpu。(3我是随便选的。我也没做过比较) gzip_min_length: 一个阈值,字节为单位,Content-Length小于该值的http响应就不压缩。(比如说有一个css文件大小只有989B,那么就会直接传输,而不进行压缩) 由于该阈值是对比Content-Length字段,而只有对于静态文件(比如css、js),nginx才能知道Content-Length,才有的比较。动态生成的页面是无法预知Content-Length的,所以照样会被压缩。 gzip_type: 需要进行gzip压缩的MIME类型,text/html类型是默认都要压缩的所以没必要再写。需要压缩的主要是纯文本,如css、js、json。图像则不需要进行压缩,因为jpg、gif、png这些图像格式本身就是压缩格式的。再用gzip压缩一来压缩不了多少质量,二是反而拖慢速度。当然,我没做过试验哈哈,我就是看其他网站也都没有对图像进行gzip压缩的。 gzip_disable: 正则表达式,匹配请求头的User-Agent字段,匹配到的则不进行gzip。主要用来排除那些不支持gzip解压的古董浏览器。 gzip_vary: on的话,则会在响应头中加上一项 Vary: Accept-Encoding。该字段的作用是明确告知缓存服务器(如果有的话)按照客户端Accept-Encoding字段的内容(即客户端是否支持gzip解压),分别给其传输对应的压缩或未压缩的内容。 另外还有其他几个配置,使用nginx默认的就好了。
安装与配置nginx+php:mac os篇
1. 安装Homebrew Homebrew是一个mac os中的软件包管理工具。类似其他linux系统中的apt-get、yum。 在mac中安装homebrew的方法请参考http://brew.sh/index_zh-cn.html 2. 安装nginx 2.1 $brew install nginx 漫长的等待,等她装完。 2.2 nginx的命令 $nginx // 启动nginx $nginx –s stop $nginx –s reload 配置文件在 /usr/local/etc/nginx/nginx.conf 3. 安装php Mac自带是有php,无需重新安装。 3.1 $php-fpm,弹出如下错误: 没有找到php-fpm.conf文件,但实际上该目录下是有一个php-fpm.conf.default文件的。 $sudo cp /private/etc/php-fpm.conf.default /private/etc/php-fpm.conf 3.2 $php-fpm,又有如下错误: 错误的原因是php-fpm.conf的日志目录不存在。 按下图所示修改/private/etc/php-fpm.conf 3.3 $php-fpm 运行成功 3.4 但此时的php-fpm运行时没有加载到php.ini配置文件。执行如下命令 $sudo cp /etc/php.ini.default /etc/php.ini 4. 配置一个php网站 4.1 新建目录 /Users/funway/project/www/tangyuan 4.2 在该目录下新建一个php文件info.php,内容如下: <?php phpinfo(); ?> 4.3 修改/usr/local/etc/nginx/nginx.conf,添加如下内容 4.5 访问locahost:9001即可访问新建的网站了
安装与配置nginx+php+mysql:centOS篇
1. 安装nginx a.添加nginx的软件源,新建文件/etc/yum.repos.d/nginx.repo b.在该文件中填写如下内容: [nginx] name=nginx repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=0 enabled=1 c.执行yum安装命令: yum install nginx d.启动nginx服务: service nginx start //或者 /etc/init.d/nginx start e.常用的nginx命令: /etc/init.d/nginx start //或者service nginx start /etc/init.d/nginx stop //或者 service nginx stop /etc/init.d/nginx restart //或者 service nginx restart 配置文件在 /etc/nginx/nginx.conf 2. 安装mysql a.输入安装命令: yum install mysql mysql-server 其中mysql表示客户端、mysql-server表示服务端 b.启动mysql服务: service mysqld start // 或者使用 /etc/init.d/mysqld start 一样 c.设置mysql服务随系统启动: chkconfig mysqld on d.使用 /usr/bin/mysql_secure_installation 命令设置mysql的root密码以及禁止匿名访问等设置。 e.测试登录mysql: 输入mysql –u root –p 回车后再输入密码 使用命令”show databases; ”查看现有的数据库 f.允许root远程登录 mysql默认是不允许root账户的远程登录的,我们可以使用sql语句 grant all privileges on *.* to 'root'@'%' identified by '123456' with grant option; 来允许任意主机使用root账户(密码123456)来访问数据库。 查看mysql的user表: 其中第四行host=’%’, user=’root’就表示允许所有主机使用root账户登录。 g.如果要关闭root账户的远程登录权限,则可以直接删除该行数据,然后刷新mysql的系统权限。 delete from mysql.user where host='%' and user='root'; flush privileges; 当然,在生产环境中,最好是要关闭数据库的远程登录。然后可以在服务器端配置一个phpMyAdmin,来提供远程管理数据库。 3. 安装php a.添加软件源(因为可能有些php组件CentOS默认的软件源里找不到) rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm 安装好后可以ls /etc/yum.repos.d目录下多了以下三个文件。 b.键入命令 yum install php php-fpm, 安装php以及FastCGI管理器php-fpm。 c.安装其他php扩展(可选,一般装个php-mysql, php-mbstring就差不多了) yum install php-gd php-mysql php-mbstring php-xml php-mcrypt d.启动php-fpm并设置为随机启动 service php-fpm start chkconfig php-fpm on 4. 配置一个php网站 a.新建一个网站目录/alidata/www/test_site b.在该目录下新建一个php文件info.php,内容如下: <?php phpinfo(); ?> c.新建一个与该网站对应的nginx配置文件: cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/test_site.conf d.修改新建的conf文件,如下图所示,黄色的为修改的,红色表示要删除前面的#号注释。 e.重启nginx服务:service nginx restart。然后就能通过ip+端口号来访问该网站了。http://your-ip:9001/info.php 5. 多个域名共用一个ip与80端口号。 情况是这样的,我有两个域名,两个网站,但只有一个服务器亦即只有一个公网ip。本来在本机测试的时候可以使用不同端口号的形式。 亦即网站A使用http://ip:9001;网站B使用http://ip:9002来访问。但不幸的是,域名服务商的域名解析只能到ip为止,不再细分端口号,只能使用80端口。即我的两个域名:www.domain-1.com与www.domain-2.com都只能解析到公网ip的默认端口80。 这样的话,我们可以利用nginx配置文件.conf中的server_name来让nginx识别不同的域名,以转向不同的目录。 建立两个网站目录 /www/domain-1与/www/domain-2,在目录内分别写一个php或index.html文件; 在/etc/nginx/conf.d目录下新建两个conf配置文件www.domain-1.conf与www.domain-2.conf; 以我们hawu.me的conf文件为例,其中listen为默认的80端口,server_name为要识别的域名 重启nginx服务,然后就能通过不同的域名来访问网站了,www.domain-1.com或者www.domain-2.com。