Nginx别名因try\u文件$uri别名错误而中断

Nginx别名因try\u文件$uri别名错误而中断,nginx,nginx-config,Nginx,Nginx Config,我有一个版本化的Symfony API实例,我希望以以下方式配置该实例: api.com/api/v1->/srv/api-v1/public/index.php api.com/api/v2->/srv/api-v2/public/index.php 我尝试使用nginx位置和别名来实现这一点,因为在默认设置为index.php之前,我们使用try\u files()检查实际文件 问题 似乎有一个函数使用别名和try\u文件来中断$uri变量 我怎样才能绕过这个bug来达到我想要的结果 n

我有一个版本化的Symfony API实例,我希望以以下方式配置该实例:

  • api.com/api/v1->/srv/api-v1/public/index.php
  • api.com/api/v2->/srv/api-v2/public/index.php
我尝试使用nginx位置和别名来实现这一点,因为在默认设置为
index.php
之前,我们使用try\u files()检查实际文件

问题 似乎有一个函数使用
别名和
try\u文件
来中断
$uri
变量

我怎样才能绕过这个bug来达到我想要的结果

nginx conf

server {
    listen 443 http2;
    listen [::]:443 http2;
    server_name api.com;
    root /srv/default/public/; # default root when no version
 
    location /api/v1 {
        alias /srv/api-v1/public/;
        try_files $uri /index.php$is_args$args;
    }

    location /api/v2 {
        alias /srv/api-v2/public/;
        try_files $uri /index.php$is_args$args;
    }

    location ~ ^/index\.php(/|$) {
        include /etc/nginx/fastcgi.conf;
        fastcgi_pass unix:/run/php-fpm-php7.2.socket;
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        internal;
        fastcgi_read_timeout 300;
    }
}
试图修复 接下来,我创建了以下配置文件,虽然可以工作,但会生成一个巨大的配置文件,这并不理想:

upstream v1 {
    server 127.0.0.1;
}
upstream v2 {
    server 127.0.0.1;
}
server {
    listen 443 http2;
    listen [::]:443 http2;
    server_name api.com;
 
    location /api/v1 {
        proxy_pass http://v1;
    }

    location /api/v2 {
        proxy_pass http://v2;
    }
}
server {
    server_name v1;
    root /srv/api-v1/public/;

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

    location ~ ^/index\.php(/|$) {
        include /etc/nginx/fastcgi.conf;
        fastcgi_pass unix:/run/php-fpm-php7.2.socket;
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        internal;
    }
}
server {
    server_name v2;
    root /srv/api-v2/public/;

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

    location ~ ^/index\.php(/|$) {
        include /etc/nginx/fastcgi.conf;
        fastcgi_pass unix:/run/php-fpm-php7.2.socket;
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        internal;
    }
}


alias
指令与
try\u files
指令一起使用时,可以使用另一种变通方法(请参见答案中的示例)。你能试试下面的配置吗

服务器{
听443http2;
听[:]:443 http2;
服务器名称api.com;
root/srv/default/public/;#无版本时的默认根目录
位置~^/api/v1(?/*){
别名/srv/api-v1/public;
try_files$v1route/api/v1/index.php$is_args$args;
位置~^/api/v1/index\.php${
内部的;
包括/etc/nginx/fastcgi.conf;
fastcgi_param SCRIPT_FILENAME/srv/api-v1/public/index.php;
fastcgi_读取超时300;
fastcgi_pass unix:/run/php-fpm-php7.2.socket;
}
}
位置~^/api/v2(?/*){
别名/srv/api-v2/public;
try_文件$v2route/api/v2/index.php$is_args$args;
位置~^/api/v2/index\.php${
内部的;
包括/etc/nginx/fastcgi.conf;
fastcgi_param SCRIPT_FILENAME/srv/api-v2/public/index.php;
fastcgi_读取超时300;
fastcgi_pass unix:/run/php-fpm-php7.2.socket;
}
}
}
临时更新(将随说明扩展)

OP提出了另一个问题:

Symfony期望的默认值有一个小问题。以下三个服务器变量
$\u server['DOCUMENT\u URI']
$\u server['SCRIPT\u NAME']
$\u server['PHP\u SELF']
equal
/api/v1/index.PHP
但是默认的Symfony nginx生成
/index.PHP
,有没有办法在上面进行调整

我不认为这是Symfony行为不正确的原因。虽然这些变量当然可以调整,但最可能的原因是不正确的
$\u SERVER['REQUEST\u URI']
值。使用上面的配置,它将等于
/api/v1/some/path
,但Symfony最有可能期望的是
/some/path
。以下是您可以尝试覆盖该变量的配置:

map $request_uri $api_ver {
    ~^/api/v([12])/?  $1;
}
map $request_uri $api_route {
    ~^/api/v[12](/[^?]*)?(?:$|\?)  $1;
}
server {
    listen 443 http2;
    listen [::]:443 http2;
    server_name api.com;
    root /srv/default/public; # default root when no version

    location ~ ^/api/v[12]/? {
        alias /srv/api-v$api_ver/public;
        try_files $api_route /api/v$api_ver/index.php$is_args$args;
        location ~ ^/api/v[12]/index\.php$ {
            internal;
            include /etc/nginx/fastcgi.conf;
            fastcgi_param REQUEST_URI $api_route$is_args$args;
            fastcgi_param SCRIPT_FILENAME /srv/api-v$api_ver/public/index.php;
            fastcgi_read_timeout 300;
            fastcgi_pass unix:/run/php-fpm-php7.2.socket;
        }
    }
}
我认为这应该可以解决您的问题,但是如果您真的想调整
$\u服务器['DOCUMENT\u URI']
$\u服务器['SCRIPT\u NAME']
$\u服务器['PHP\u SELF']
,您可以在嵌套位置添加两行额外的代码:

        location ~ ^/api/v[12]/index\.php$ {
            internal;
            include /etc/nginx/fastcgi.conf;
            fastcgi_param REQUEST_URI $api_route$is_args$args;
            fastcgi_param DOCUMENT_URI /index.php;
            fastcgi_param SCRIPT_NAME /index.php;
            fastcgi_param SCRIPT_FILENAME /srv/api-v$api_ver/public/index.php;
            fastcgi_read_timeout 300;
            fastcgi_pass unix:/run/php-fpm-php7.2.socket;
        }
但我不认为这是正确的Symfony行为所必需的

更新2

要防止
$\u服务器['REQUEST\u URI']
变量为空字符串,可以使用以下选项:

  • 将请求从
    /api/v1
    /api/v2
    重定向到
    /api/v1/
    /api/v2/
    (对我来说似乎是最好的):

  • 在确切的
    /api/v1
    /api/v2
    请求中明确添加尾部斜杠:

    map $request_uri $api_ver {
        ~^/api/v([12])/?  $1;
    }
    map $request_uri $api_route {
        ~^/api/v[12](?:/([^?]*))?(?:$|\?)  /$1;
    }
    server {
        ...
        location ~ ^/api/v[12]/? {
            alias /srv/api-v$api_ver/public;
            try_files $api_route /api/v$api_ver/index.php$is_args$args;
            location ~ ^/api/v[12]/index\.php$ {
                internal;
                include /etc/nginx/fastcgi.conf;
                fastcgi_param REQUEST_URI $api_route$is_args$args;
                fastcgi_param SCRIPT_FILENAME /srv/api-v$api_ver/public/index.php;
                fastcgi_read_timeout 300;
                fastcgi_pass unix:/run/php-fpm-php7.2.socket;
            }
        }
    }
    
  • 如果
    $\u服务器['REQUEST\u URI']
    变量值前面应该有
    /api
    字符串,您可以尝试
    fastcgi\u参数REQUEST\u URI/api$api\u route$is\u args$args
    而不是
    fastcgi\u-param-REQUEST\u-URI$api\u-route$is\u-args$args

    如果要调整
    $\u服务器['DOCUMENT\u URI']
    $\u服务器['SCRIPT\u NAME']
    $\u服务器['PHP\u SELF']
    变量,请添加

    fastcgi_param DOCUMENT_URI /index.php;
    fastcgi_param SCRIPT_NAME /index.php;
    
    行到嵌套位置。

    在“hacky fix”中,您缺少
    代理传递
    指令的尾部
    /
    。 以下是一个repo,可以轻松测试您的实现:


    查看是否需要有关尾随斜杠的更多信息。

    如果您对“hacky fix”没有问题,可以将其弄得更干净一些。只需将重复配置放入其他文件中。然后可以使用
    include
    指令导入另一个文件

    /etc/nginx/some\u other\u文件:


    您可以将
    if
    $request\u filename
    别名一起使用,以避免使用
    try\u文件
    。您还需要为PHP URI使用嵌套位置。请参阅。注意在位置上下文中使用if。请参阅测试,到目前为止,这似乎如预期的那样工作,是一个更简单的解决方案。非常感谢。是的,大多数变量都是通过
    /etc/nginx/fastcgi.conf
    (基于Debian)或
    /etc/nginx/fastcgi_params
    (基于RHEL)文件定义为默认值的。您可以重新定义它们(仅在包含文件
    fastcgi.conf
    之后,请参阅答案以获取更多解释)。对我来说,PHP应用程序的行为可能依赖于
    请求URI
    文档URI
    之外的任何其他变量,这似乎很奇怪。我很快会对答案进行更新,但现在没有时间。我昨天开始写一个非常大的答案,但系统崩溃:(您要求的是一个小的调整,您需要在
    include/etc/nginx/fastcgi.conf;
    行之后的两个嵌套位置添加
    fastcgi\u-param-DOCUMENT\u-URI/index.php;
    fastcgi-param-SCRIPT\u-NAME/index.php;
    行,但我担心您的问题来自
    REQUEST\u-URI的值不正确。)变量(您需要剥离
    /api/vN
    substri
    fastcgi_param DOCUMENT_URI /index.php;
    fastcgi_param SCRIPT_NAME /index.php;
    
        location / {
            try_files $uri /index.php$is_args$args;
        }
    
        location ~ ^/index\.php(/|$) {
            include /etc/nginx/fastcgi.conf;
            fastcgi_pass unix:/run/php-fpm-php7.2.socket;
            fastcgi_split_path_info ^(.+?\.php)(/.*)$;
            internal;
        }
    
    upstream v1 {
        server 127.0.0.1;
    }
    upstream v2 {
        server 127.0.0.1;
    }
    server {
        listen 443 http2;
        listen [::]:443 http2;
        server_name api.com;
     
        location /api/v1 {
            proxy_pass http://v1;
        }
    
        location /api/v2 {
            proxy_pass http://v2;
        }
    }
    server {
        server_name v1;
        root /srv/api-v1/public/;
        include some_other_file;
    }
    server {
        server_name v2;
        root /srv/api-v2/public/;
        include some_other_file;
    }