Nginx 如果分割目录中存在GZIP缓存文件,则重写,否则发送到PHP;还将bot和未启用GZIP的请求传递给PHP

Nginx 如果分割目录中存在GZIP缓存文件,则重写,否则发送到PHP;还将bot和未启用GZIP的请求传递给PHP,nginx,Nginx,我正在寻找一种方法,使下面的“重写”/“名称…”仅在重写目标存在(是文件)时发生 我试过使用try_文件,但它抛出了一个404 location ~* "^/names/(.*?)([^/]{2})([^/]+)" { try_files /cache/html/names/$1$2/$3.html.gz /data/html/names/$1$2/$3.html.gz; if ( $http_accept_encoding ~ "gzip"

我正在寻找一种方法,使下面的“重写”/“名称…”仅在重写目标存在(是文件)时发生

我试过使用try_文件,但它抛出了一个404

location ~* "^/names/(.*?)([^/]{2})([^/]+)" {
    try_files /cache/html/names/$1$2/$3.html.gz /data/html/names/$1$2/$3.html.gz;
    if ( $http_accept_encoding ~ "gzip" )
    {
        rewrite "/names/(.*?)([^/]{2})([^/]+)$" /cache/html/names/$1$2/$3.html.gz last;
        break;
    }
    rewrite ^/(.*)$ /index.php?request=$1 last;
    break;
}
更新

这是Apache的代码。注意:它的开头还有一个语言ISO代码,例如/de/。但是英语没有语言代码

RewriteCond %{QUERY_STRING} ^request=([a-z\/]*)names\/(.*?)([^/]{3})([^/]+) [NC]
RewriteRule .* - [E=LANG:%1,E=SN:%2%3/%4]
RewriteCond %{ENV:PATH}/cache/html/%{ENV:LANG}names/%{ENV:SN}.html -f
RewriteRule .* cache/html/%{ENV:LANG}names/%{ENV:SN}.html [L]
完整解决方案

这是针对我的情况的完整解决方案

我有一些URL,它们有一个可选的语言组件,后跟“name”,后跟一个名称,例如“ivanov”

例如:

/names/ivanov
/de/names/ivanov
这些文件作为以下文件发送到php:

/index.php?request=/de/names/ivanov
PHP将根据某些标准决定是否将名称缓存为.html.gz文件

文件缓存在
/cache/html/names/

由于存储问题,这些文件被分成由名称的前两个字符组成的目录:

/names/ivanov => /cache/html/names/iv/anov.html.gz
而1-2个字符的名称保存为:

/names/ho => /cache/html/names/ho.html.gz
由于缓存保存为GZIP(以节省约80-90%的磁盘空间),因此需要将任何不接受GZIP的请求推送到PHP而不是缓存

还需要将所有请求从定义的IP列表推送到PHP(而不是GZIP缓存)。这些是用于自动刮片的IP。当检测到它们时,我将它们分配给nginx中的cgi_fastparam。这是在PHP$\u服务器变量中获取的;然后我将随机数据提供给自动刮取机器人。因此,不应允许这些IP查看具有正确变量的GZIP HTML缓存

我使用这种方法,因为流量一度是75%的机器人。通过阻止IP,刮取器能够判断何时没有获得所需的数据。他们可以调整其刮取或增加IP数。但是通过提供随机数据,您完全否定了他们所收集的所有数据的有效性。这导致刮刀的数量减少了100%

代码(感谢下面Ivan的帮助):

在nginx服务器配置之外添加:

map $name $gzfile {
    default $name.html.gz;
    "~^(.{2})(.+)$" $1/$2.html.gz;
}

geo $scrapers { default 0; 140.227.198.242 1; 127.0.0.1 1; }
映射块根据请求设置值。它删除请求+.html.gz;但如果请求包含三个或更多字符,则将其拆分为iv/anov.html.gz

geo$scrapers块是一个已被识别为自动机器人的IP地址列表。默认情况下,刮刀设置为0;但如果远程主机是已定义的刮板,则会将其分配给1

location ~* "^/([a-z\/]*)names/(.*?)([^/]+)$" {
    set $lang $1;
    set $page "names";
    set $path $2;
    set $name $3;
    if ($http_accept_encoding !~ gzip) {
        rewrite ^/(.*)$ /index.php?request=$1 last;
    }
    if ($scrapers = 1) {
        rewrite ^/(.*)$ /index.php?request=$1 last;
    }
    try_files /data/cache/html/$lang$page/$path$gzfile /index.php?request=$lang$page/$path$name;
    add_header  Content-Encoding  gzip;
    gzip off;
    default_type text/html;
}
位置块拾取匹配的URL:
“^/([a-z\/]*)名称/(.*)([^/]+)$
。这是一个可选的(*)语言定义,例如“de”,“ru”

变量由位置正则表达式中的三个(括号)设置

第一个if块检查请求是否不接受GZIP压缩,以及是否将请求重定向到PHP。最后一个标志停止正在处理的块中的进一步代码

第二个块检查访问服务器的IP是否定义为刮板,如果定义为刮板,则将请求推送到PHP,并且不在块中执行进一步的代码

try_files行检查是否存在GZIP HTML缓存并将其加载;否则将请求推送到PHP

最后三行将内容设置为GZIP编码和HTML mime类型。这告诉浏览器压缩内容,响应为HTML。否则浏览器将下载文件或将其显示为GZIP文本

location ~ \.php$
{
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.2-fpm.sock;
        fastcgi_param   SCRAPER $scrapers;
}
上述内容也添加到服务器块中,用于处理PHP。fastcgi_pass和include可能需要与您的系统有所不同


fastcgi_参数行添加了一个变量,可以通过$_服务器['SCRAPER']在PHP中访问该变量。如果在上面列出的nginx geo块中定义了该变量,则该变量将等于“1”;否则等于“0”。

如果我正确理解您需要什么,请尝试以下操作:

location ~* "^/names/(.*?)([^/]{2})([^/]+)$" {
    set $prefix $1$2;
    set $name $3;
    if ($http_accept_encoding !~ gzip) {
        rewrite ^/(.*)$ /index.php?request=$1 last;
    }
    try_files /cache/html/names/$prefix/$name.html.gz /index.php?request=names/$prefix$name;
}
请注意,
try\u files
指令的最后一个参数的含义与所有其他参数不同。作为文档:

如果没有找到任何文件,则会对最后一个参数中指定的uri进行内部重定向

另外,您对
last
break
的使用有点奇怪,在
rewrite…last
之后在
location
块内使用
break
指令没有任何意义$/index.php?request=$1 last;使nginx立即退出当前
位置
块,并搜索适合
/index.php?request=…
URI的
位置

更新

根据新OP的要求,更新了解决方案

备选案文1(丑):

变量2(使用
map
):


我是否正确理解您的算法?如果((接受gzip编码)和(.html.gz文件存在),则使用.html.gz文件进行响应,否则使用/index.php进行响应?请求=?此外,您是否错过了
^/names/(.*)([^/]{2})([^/]+)结尾的问号
在您的
位置中的表达式
定义?谢谢。它工作得近乎完美;而且更加精简。如果这里有一个设施可以给点分数,我会的。您正确理解我需要做什么。就我所知,只有一个问题。对超过2个字符的名称的请求,如/names/shatsky,将是如果存在,则返回到缓存;如果不存在,则返回index.php(生成并缓存页面的位置)。但是,如果名称为1-2个字符(/names/ho),它总是转到index.php。这不是一个大问题,因为只有676个这样的页面。我会看看我是否能做些什么……据我所知,为
/names/shatsky
请求生成的页面将是
/cache/html/names/sh/atsky.html.gz
。将为
/names/sh
/names/s
生成哪些页面请求?
/names/shlocation ~* "^/names/(.*?)([^/]{2})([^/]+)$" {
    set $prefix $1$2;
    set $name $3;
    if ($http_accept_encoding !~ gzip) {
        rewrite ^/(.*)$ /index.php?request=$1 last;
    }
    try_files /cache/html/names/$prefix/$name.html.gz /index.php?request=names/$prefix$name;
}
location ~* "^/names/(.*?)([^/]{1,2})([^/]*)$" {
    set $path $1;
    set $prefix "";
    set $name $2.html.gz;
    set $suffix $3;
    set $full $1$2$3;
    if ($suffix) {
        set $prefix $2/;
        set $name $3.html.gz;
    }
    if ($http_accept_encoding !~ gzip) {
        rewrite ^/(.*)$ /index.php?request=$1 last;
    }
    try_files /cache/html/names/$path$prefix$name /index.php?request=names/$full;
}
map $name $gzfile {
    default $name.html.gz;
    "~^(.{2})(.+)$" $1/$2.html.gz;
}

server {
    ...
    location ~* "^/names/(.*?)([^/]+)$" {
        set $path $1;
        set $name $2;
        if ($http_accept_encoding !~ gzip) {
            rewrite ^/(.*)$ /index.php?request=$1 last;
        }
        try_files /cache/html/names/$path$gzfile /index.php?request=names/$path$name;
    }
    ...
}