首页 > 解决方案 > 如果 GZIP 缓存文件存在于拆分目录中,则重写,否则发送到 PHP;还将机器人和非 GZIP 启用的请求传递给 PHP

问题描述

我正在寻找一种方法来仅在重写目标存在(是文件)时才发生以下重写“/names ...”。

location ~* "^/names/(.*?)([^/]{2})([^/]+)" {
    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;
}

我试过使用 try_files,但它会抛出 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 有一个可选的语言组件,后跟“名称”,然后是一个名称,例如“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 $_SERVER 变量中提取的;然后我向自动抓取机器人提供随机数据。因此,不应允许这些 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 地址列表。默认情况下,scrapers 设置为 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\/]*)names/(.*?)([^/]+)$。即带有可选 (*) 语言定义,例如“de”、“ru”。

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

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

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

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

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

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_param 行添加了一个可以在 PHP 中通过 $_SERVER['SCRAPER'] 访问的变量。如果它在上面列出的 nginx 地理块中定义,它将等于“1”;否则为“0”。

标签: nginx

解决方案


如果我正确理解你需要什么,试试这个:

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_files指令的最后一个参数与其他所有参数的含义不同。正如文件所说

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

此外,您对lastand的使用break有点奇怪,在块内使用break指令后没有任何意义。这是文档。使 nginx 立即退出当前块并搜索适合URI 的块。rewrite ... lastlocationrewrite ^/(.*)$ /index.php?request=$1 last;locationlocation/index.php?request=...

更新

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

变体1(丑陋):

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

变体 2(使用map):

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

推荐阅读