Nginx 别名由于 try_files $uri 别名错误而中断 [英] Nginx alias breaks due to try_files $uri alias bug
问题描述
我有一个版本化的 Symfony API 实例,我想按以下方式配置:
I have a versioned Symfony API instance that I want to configure in the following manner:
- api.com/api/v1 ->/srv/api-v1/public/index.php
- api.com/api/v2 ->/srv/api-v2/public/index.php
- api.com/api/v1 -> /srv/api-v1/public/index.php
- api.com/api/v2 -> /srv/api-v2/public/index.php
我尝试使用 nginx 位置和别名来解决这个问题,因为它是 Symfony,我们使用 try_files (推荐) 在默认为 index.php
之前检查实际文件.
I've tried to approach this using nginx location and aliases, as it's Symfony we use try_files (as recommended) to check for an actual file prior to defaulting to index.php
.
似乎有一个已知的nginx错误破坏了$uri
变量,带有 alias
和 try_files
.
It seems there is a known nginx bug that breaks the $uri
variable with an alias
and try_files
.
我怎样才能绕过这个错误来达到我想要的结果?
How can I get around this bug to achieve my desired outcome?
nginx 配置
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;
}
}
尝试修复
通过这个hacky修复我创建了以下可以工作但生成一个巨大的配置文件,不理想的:
Attempted fix
Going by this hacky fix I have created the following which does work but generates a huge config file, not ideal:
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
指令结合使用时,存在另一种解决方法> 一个(参见 this 答案示例).你可以试试下面的配置吗?
There is another workaround exists which can be used when an alias
directive is used in conjunction with the try_files
one (see this answer for an example). Can you try the following config?
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 提出了一个额外的问题:
OP asks an additional question:
Symfony 期望的默认值存在一个小问题.以下三个服务器变量 $_SERVER['DOCUMENT_URI']
、$_SERVER['SCRIPT_NAME']
和 $_SERVER['PHP_SELF']
相等/api/v1/index.php
但默认的 Symfony nginx 生成 /index.php
,有没有办法在上面调整它?
There's one slight issue with the defaults Symfony expects. The following three server variables
$_SERVER['DOCUMENT_URI']
,$_SERVER['SCRIPT_NAME']
and$_SERVER['PHP_SELF']
equal/api/v1/index.php
but default Symfony nginx generates/index.php
, is there a way to tweak that in the above?
我不认为这是 Symfony 行为不正确的原因.虽然这个变量当然可以调整,但最可能的原因是 $_SERVER['REQUEST_URI']
值不正确.使用上面的配置,它将等于 /api/v1/some/path
但很可能 Symfony 只期望 /some/path
代替.以下是您可以尝试覆盖该变量的配置:
I don't think this is the reason of incorrect Symfony behavior. While this variables of course can be tweaked, the most probable reason is incorrect $_SERVER['REQUEST_URI']
value. With the configuration above it will be equal to /api/v1/some/path
but most likely Symfony expects just /some/path
instead. Here is the configuration you can try to overwrite that variable:
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']
,可以在嵌套位置多加两行:
I think this should fix your issues, but if you really want to tweak $_SERVER['DOCUMENT_URI']
, $_SERVER['SCRIPT_NAME']
and $_SERVER['PHP_SELF']
, you can add two additional lines to the nested location:
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 行为所必需的.
but I don't think it is required for correct Symfony behavior.
更新 2
为了防止 $_SERVER['REQUEST_URI']
变量成为空字符串,您有以下选项:
To prevent $_SERVER['REQUEST_URI']
variable from being an empty string, you have the following options:
将请求从
/api/v1
或/api/v2
重定向到/api/v1/
或/api/v2/
(对我来说似乎是最好的):
Redirect the request from
/api/v1
or/api/v2
to/api/v1/
or/api/v2/
(seems the best as for me):
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;
}
}
}
明确地将尾部斜杠添加到确切的 /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;
.
If the $_SERVER['REQUEST_URI']
variable value should be prepended with /api
string, you can try fastcgi_param REQUEST_URI /api$api_route$is_args$args;
instead of fastcgi_param REQUEST_URI $api_route$is_args$args;
.
如果你想调整 $_SERVER['DOCUMENT_URI']
、$_SERVER['SCRIPT_NAME']
和 $_SERVER['PHP_SELF']
代码>变量,添加
If you want to tweak $_SERVER['DOCUMENT_URI']
, $_SERVER['SCRIPT_NAME']
and $_SERVER['PHP_SELF']
variables, add the
fastcgi_param DOCUMENT_URI /index.php;
fastcgi_param SCRIPT_NAME /index.php;
到嵌套位置的行.
这篇关于Nginx 别名由于 try_files $uri 别名错误而中断的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!