Nginx 别名由于 try_files $uri 别名错误而中断 [英] Nginx alias breaks due to try_files $uri alias bug

查看:60
本文介绍了Nginx 别名由于 try_files $uri 别名错误而中断的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个版本化的 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 变量,带有 aliastry_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:

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

  1. 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屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆