搭建了一个禅道的项目管理平台,但是Nginx下并不是伪静态的,URL看着不是很舒服,上网搜索配置规则,但是没有一个可以正常使用的,于是乎自己琢磨一下。

先给出最终配置,有兴趣看原因的可以往下看

server {
    listen 80;
    root /zentao/root/path/www;
    index index.php index.html index.htm;
    server_name domain_name;

    location / {
        try_files $uri /index.php$uri;
    }

    location ~ \.php(/.+)?$ {
        fastcgi_pass unix:/var/run/php-fpm.sock;
        fastcgi_index index.php;
        include fastcgi.conf;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    access_log off;
}

禅道给出了apache的伪静态规则,但是我尝试了半天也没有转换成可用的nginx伪静态规则,但是禅道是基于PHP的,而PHP伪静态一般基于$_SERVER变量中的一些变量来实现的,既然apache可以实现伪静态,那么就打印一下伪静态后的$_SERVER变量的值。

修改禅道根目录下www/index.php文件,在最上面修改为如下代码:

<?php
    echo "<pre>";
    print_r($_SERVER);
    echo "</pre>";
    die;

然后访问http://domain/user-login-Lw==.html (此为禅道登陆url)

// 仅给出几个重要的变量
Array
(
    [REDIRECT_URL] => /user-login-Lw==.html
    [REQUEST_URI] => /user-login-Lw==.html
    [SCRIPT_NAME] => /index.php
    [PATH_INFO] => /user-login-Lw==.html
    [PATH_TRANSLATED] => redirect:/index.php/user-login-Lw==.html
    [PHP_SELF] => /index.php/user-login-Lw==.html
)

对于未配置过的nginx做相同的操作:

未配置的nginx并不是什么都没配置,基本的还是要配置的

    location / {
        try_files $uri /index.php;
    }
    location ~ \.php$ {
        # With php5-fpm:
        fastcgi_pass unix:/var/run/php-fpm.sock;
        fastcgi_index index.php;
        include fastcgi.conf;
    }

如上,这两个location还是要有的,一个是将所有的请求重定向到index.php,一个是解析php文件的.

这次,php返回的信息为

// 仅给出几个重要的变量
Array
(
    [SCRIPT_NAME] => /index.php
    [REQUEST_URI] => /user-login-Lw==.html
    [DOCUMENT_URI] => /index.php
    [PHP_SELF] => /index.php
)

对比一下,发现不同了吧。nginx少了PATH_INFO,而且PHP_SELF也是不一样的,至于REDIRECT_URLPATH_TRANSLATED这些变量都是apache自己加的,禅道并不会用到。

回想一下禅道的配置文件config/config.phpconfig/my.php中有一项$config->requestType配置,如果配置为**GET的话,就会禁用伪静态,而通过query string的方式,如果配置成PATH_INFO**的话就需要nginx或apache配置伪静态规则,也就是说禅道通过解析PATH_INFO来实现伪静态的。

那么PATH_INFO又是什么东东,根据PHP官方文档的解释,如果我访问http://www.example.com/php/path_info.php/some/stuff?foo=bar这个url,那个PATH_INFO就应该等于/some/stuff,也就是所PATH_INFO等于url中跟在真实脚本名称之后并且在查询语句(query string)之前的字符串,你可能会说,我访问的url中根本没有filename.php啊,那是因为nginx帮你添加了,记得配置中的try_files $uri /index.php$uri吧,这个内部跳转就是干这个事情的。

找到了原因就开始解决,Nginx和PHP是通过cgi协议传递数据的,可以通过nginx开启debug (http://nginx.org/en/docs/debugging_log.html)来查看传递nginx和php交换的数据。

这里不给出详细debug log了,反正开启log后发现nginx并没有传递PATH_INFO给php,于是在解析php的location中添加一行

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm.sock;
        fastcgi_index index.php;
        include fastcgi.conf;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

但只有这个还是不行,因为此时nginx内部变量$uri(就相当于PHP_SELF的值)还是*/index.php*,需要再修改一个地方

    location / {
        try_files $uri /index.php;
    }

修改之后php那个location就没办法被识别了,需要修改为\.php(/.+)?$

最终的效果就是

server {
    listen 80;
    root /zentao/root/path/www;
    index index.php index.html index.htm;
    server_name domain_name;

    location / {
        try_files $uri /index.php$uri;
    }

    location ~ \.php(/.+)?$ {
        fastcgi_pass unix:/var/run/php-fpm.sock;
        fastcgi_index index.php;
        include fastcgi.conf;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    access_log off;
}