由于try_files$uri别名错误,Nginx别名中断



我有一个版本化的Symfony API实例,我想用以下方式配置它:

  • api.com/api/v1->/srv/api-v1/public/index.php
  • api.com/api/v2->/srv/api-v2/public/index.php

我曾尝试使用nginx位置和别名来解决此问题,因为它是Symfony,我们在默认为index.php之前使用try_files(如建议(来检查实际文件。

问题

似乎有一个已知的nginx错误用aliastry_files破坏了$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_files指令结合使用时,可以使用另一种解决方法(请参阅此答案以获取示例(。你能试试下面的配置吗?

server {
listen 443 http2;
listen [::]:443 http2;
server_name api.com;
root /srv/default/public/; # default root when no version

location ~ ^/api/v1(?<v1route>/.*)? {
alias /srv/api-v1/public;
try_files $v1route /api/v1/index.php$is_args$args;
location ~ ^/api/v1/index.php$ {
internal;
include /etc/nginx/fastcgi.conf;
fastcgi_param SCRIPT_FILENAME /srv/api-v1/public/index.php;
fastcgi_read_timeout 300;
fastcgi_pass unix:/run/php-fpm-php7.2.socket;
}
}
location ~ ^/api/v2(?<v2route>/.*)? {
alias /srv/api-v2/public;
try_files $v2route /api/v2/index.php$is_args$args;
location ~ ^/api/v2/index.php$ {
internal;
include /etc/nginx/fastcgi.conf;
fastcgi_param SCRIPT_FILENAME /srv/api-v2/public/index.php;
fastcgi_read_timeout 300;
fastcgi_pass unix:/run/php-fpm-php7.2.socket;
}
}
}

临时更新(将随解释进行扩展(

OP提出了另一个问题:

Symfony期望的默认值有一个小问题。以下三个服务器变量$_SERVER['DOCUMENT_URI']$_SERVER['SCRIPT_NAME']$_SERVER['PHP_SELF']等于/api/v1/index.php,但默认Symfony nginx生成/index.php,有没有办法在上面进行调整?

我不认为这是Symfony行为不正确的原因。虽然这些变量当然可以调整,但最可能的原因是$_SERVER['REQUEST_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;
}
}
}

我认为这应该可以解决您的问题,但如果您真的想调整$_SERVER['DOCUMENT_URI']$_SERVER['SCRIPT_NAME']$_SERVER['PHP_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

为了防止$_SERVER['REQUEST_URI']变量为空字符串,您有以下选项:

  1. 将请求从/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]$ {
    return 301 https://$host$uri/$is_args$args;
    }
    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;
    }
    }
    }
    
  2. 显式地将尾部斜线添加到确切的/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;
    }
    }
    }
    

如果$_SERVER['REQUEST_URI']变量值应以/api字符串开头,则可以尝试fastcgi_param REQUEST_URI /api$api_route$is_args$args;而不是fastcgi_param REQUEST_URI $api_route$is_args$args;

如果要调整$_SERVER['DOCUMENT_URI']$_SERVER['SCRIPT_NAME']$_SERVER['PHP_SELF']变量,请添加

fastcgi_param DOCUMENT_URI /index.php;
fastcgi_param SCRIPT_NAME /index.php;

行到嵌套位置。

在"hacky fix";您的proxy_pass指令缺少后面的/。这里有一个repo,可以轻松测试您的实现:https://github.com/MelwinKfr/nginx-workaround97

如果您想了解有关尾部斜杠的更多信息,请参阅此线程。

如果你对"破解"很满意,你可以让它更干净一点。只需将重复的配置放入其他文件即可。然后可以使用include指令导入其他文件。

/etc/nginx/some_other_file:

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;
}

最新更新