如何将相同的 RewriteCond 附加到 .htaccess 中的不同 RewriteRule?

How to attach same RewriteCond to diffirent RewriteRule in .htaccess?

我想在用户代理匹配 Twitter 或 Facebook 时重定向 url 到我的 ogp 页面。

我的重定向图片是这样的。

/news/detail.html?id=1 -> /api/v1/informations/ogp/1?lang=ja
/news/detail.html?id=1&lang=en -> /api/v1/informations/ogp/1?lang=en

/sport/detail.html?id=1 -> /api/v1/sports/ogp/1?lang=ja
/sport/detail.html?id=1&lang=en -> /api/v1/sports/ogp/1?lang=en

/event/common/detail.html?id=1 -> /api/v1/events/ogp/1?lang=ja
/event/common/detail.html?id=1 -> /api/v1/events/ogp/1?lang=ja
/event/special/detail.html?id=2&lang=en -> /api/v1/events/ogp/2?lang=en
/event/special/detail.html?id=2 -> /api/v1/events/ogp/2?lang=ja

所以我写了 htaccess,Env 参数工作正常,但是当 RewriteRule 超过两个或更多时,重写规则不起作用。

<IfModule mod_setenvif.c>
    SetEnvIfNoCase User-Agent "^facebookexternalhit.*$" UA_FACEBOOK=1
    SetEnvIfNoCase User-Agent "^facebookplatform.*$" UA_FACEBOOK=1
    SetEnvIfNoCase User-Agent "^Twitterbot.*$" UA_TWITTER=1
</IfModule>

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteBase /

    RewriteCond %{ENV:UA_FACEBOOK} ^1$ [OR]
    RewriteCond %{ENV:UA_TWITTER} ^1$
    RewriteCond %{QUERY_STRING} (^|&)id=(\d+)&lang=(\w+)($|&)
    RewriteRule ^news/detail.html$ /api/v1/informations/ogp/%2?lang=%3 [R,L]
    RewriteRule ^sport/detail.html$ /api/v1/sports/ogp/%2?lang=%3 [R,L]
    RewriteRule ^event/common/detail.html$ /api/v1/events/ogp/%2?lang=%3 [R,L]
    RewriteRule ^event/special/detail.html$ /api/v1/events/ogp/%2?lang=%3 [R,L]

    RewriteCond %{ENV:UA_FACEBOOK} ^1$ [OR]
    RewriteCond %{ENV:UA_TWITTER} ^1$
    RewriteCond %{QUERY_STRING} (^|&)id=(\d+)($|&)
    RewriteRule ^news/detail.html$ /api/v1/informations/ogp/%2?lang=ja [R,L]
    RewriteRule ^sport/detail.html$ /api/v1/sports/ogp/%2?lang=ja [R,L]
    RewriteRule ^event/common/detail.html$ /api/v1/events/ogp/%2?lang=ja [R,L]
    RewriteRule ^event/special/detail.html$ /api/v1/events/ogp/%2?lang=ja [R,L]
</IfModule>

我想用相同的 RewriteCond 重定向它们,怎么办?

即使是肮脏的代码也是受欢迎的!

这个肮脏的代码做了我想要它做的事情。

<IfModule mod_setenvif.c>
    SetEnvIfNoCase User-Agent "^facebookexternalhit.*$" UA_FACEBOOK=1
    SetEnvIfNoCase User-Agent "^facebookplatform.*$" UA_FACEBOOK=1
    SetEnvIfNoCase User-Agent "^Twitterbot.*$" UA_TWITTER=1
</IfModule>

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteBase /

    RewriteCond %{ENV:UA_FACEBOOK} ^1$ [OR]
    RewriteCond %{ENV:UA_TWITTER} ^1$
    RewriteCond %{QUERY_STRING} (^|&)id=(\d+)&lang=(\w+)($|&)
    RewriteRule ^news/detail.html$ /api/v1/informations/ogp/%2?lang=%3 [R,L]

    RewriteCond %{ENV:UA_FACEBOOK} ^1$ [OR]
    RewriteCond %{ENV:UA_TWITTER} ^1$
    RewriteCond %{QUERY_STRING} (^|&)id=(\d+)&lang=(\w+)($|&)
    RewriteRule ^sport/detail.html$ /api/v1/sports/ogp/%2?lang=%3 [R,L]

    RewriteCond %{ENV:UA_FACEBOOK} ^1$ [OR]
    RewriteCond %{ENV:UA_TWITTER} ^1$
    RewriteCond %{QUERY_STRING} (^|&)id=(\d+)&lang=(\w+)($|&)
    RewriteRule ^event/common/detail.html$ /api/v1/events/ogp/%2?lang=%3 [R,L]

    RewriteCond %{ENV:UA_FACEBOOK} ^1$ [OR]
    RewriteCond %{ENV:UA_TWITTER} ^1$
    RewriteCond %{QUERY_STRING} (^|&)id=(\d+)&lang=(\w+)($|&)
    RewriteRule ^event/special/detail.html$ /api/v1/events/ogp/%2?lang=%3 [R,L]

    RewriteCond %{ENV:UA_FACEBOOK} ^1$ [OR]
    RewriteCond %{ENV:UA_TWITTER} ^1$
    RewriteCond %{QUERY_STRING} (^|&)id=(\d+)($|&)
    RewriteRule ^news/detail.html$ /api/v1/informations/ogp/%2?lang=ja [R,L]

    RewriteCond %{ENV:UA_FACEBOOK} ^1$ [OR]
    RewriteCond %{ENV:UA_TWITTER} ^1$
    RewriteCond %{QUERY_STRING} (^|&)id=(\d+)($|&)
    RewriteRule ^sport/detail.html$ /api/v1/sports/ogp/%2?lang=ja [R,L]

    RewriteCond %{ENV:UA_FACEBOOK} ^1$ [OR]
    RewriteCond %{ENV:UA_TWITTER} ^1$
    RewriteCond %{QUERY_STRING} (^|&)id=(\d+)($|&)
    RewriteRule ^event/common/detail.html$ /api/v1/events/ogp/%2?lang=ja [R,L]

    RewriteCond %{ENV:UA_FACEBOOK} ^1$ [OR]
    RewriteCond %{ENV:UA_TWITTER} ^1$
    RewriteCond %{QUERY_STRING} (^|&)id=(\d+)($|&)
    RewriteRule ^event/special/detail.html$ /api/v1/events/ogp/%2?lang=ja [R,L]
</IfModule>

我尝试了使用 If 指令的方法,但它不适用于我的环境变量,如下面的代码

<If "%{ENV:UA_FACEBOOK} -eq 1 || %{ENV:UA_TWITTER} -eq 1"></If>

所以我通过一次又一次地编写很多相同的 RewriteCond 解决了这个问题。

如果你有更好的写法,欢迎提供。

/news/detail.html?id=1 -> /api/v1/informations/ogp/1?lang=ja
/news/detail.html?id=1&lang=en -> /api/v1/informations/ogp/1?lang=en

/sport/detail.html?id=1 -> /api/v1/sports/ogp/1?lang=ja
/sport/detail.html?id=1&lang=en -> /api/v1/sports/ogp/1?lang=en

/event/common/detail.html?id=1 -> /api/v1/events/ogp/1?lang=ja
/event/common/detail.html?id=1&lang=en -> /api/v1/events/ogp/1?lang=en

/event/special/detail.html?id=2 -> /api/v1/events/ogp/2?lang=ja
/event/special/detail.html?id=2&lang=en -> /api/v1/events/ogp/2?lang=en

这实际上只有 4 个 URL 映射(当省略 lang 参数时,它分配了一个默认值 - 目标保持不变),所以你只需要 4 个规则(如果你提取lang 参数)。最后 2 个重定向到同一个目标,因此可以轻松组合它们(使用正则表达式交替 (common|special)),只剩下 3 个需要的单独规则。

您可以这样做以避免任何重复:

RewriteEngine On
RewriteBase /api/v1

# Get "lang" (default to "ja")
RewriteRule ^ - [E=LANG:ja]
RewriteCond %{QUERY_STRING} (?:^|&)lang=(\w+)($|&)
RewriteRule ^ - [E=LANG:%1]

# Get "id" (if any)
RewriteCond %{QUERY_STRING} (?:^|&)id=(\d+)($|&)
RewriteRule ^ - [E=ID:%1]

# If not Facebook|Twitter OR no ID then skip the next 3 rules
RewriteCond %{HTTP_USER_AGENT} !^(facebook(externalhit|platform)|twitterbot) [NC,OR]
RewriteCond %{ENV:ID} ^$
RewriteRule ^ - [S=3]

# If here then Facebook|Twitter and "id" param are passed, LANG already set
RewriteRule ^news/detail\.html$ informations/ogp/%{ENV:ID}?lang=%{ENV:LANG} [R,L]
RewriteRule ^sport/detail\.html$ sports/ogp/%{ENV:ID}?lang=%{ENV:LANG} [R,L]
RewriteRule ^event/(common|special)/detail\.html$ events/ogp/%{ENV:ID}?lang=%{ENV:LANG} [R,L]

这里的技巧是使用 S (skip) 标志在 User-Agent 不是 [=52] 时跳过以下 3 条规则=] Facebook 或 Twitter(或者不存在 id URL 参数)- 颠倒逻辑。这意味着最后 3 条规则仅在 User-Agent Facebook 或 Twitter 并且存在 id URL 参数时处理。

请注意,我使用 non-capturing 子模式来确保反向引用(即 %1)仅引用我们感兴趣的组。

注意:我更改了 RewriteBase 以“简化”最后 3 条规则。尽管如果您有其他指令,那么您可能希望将其改回。

I tried the method of using the If directive, but it didn't work

您当前的规则在 <If> 指令 (Apache 2.4) 中无法匹配,因为在此 contextRewriteRule 指令与绝对 file-path,而不是相对 URL-path(可能是因为 <If> 容器合并 非常 晚)。

假设没有冲突,一个简单的解决方法是从 RewriteRule 模式的开头删除 start-of-string 锚点 (^) 以便能够匹配 /absolute/file/path/to/public_html/news/detail.php 形式的 file-path。 (虽然我可能会添加一个斜杠前缀以避免任何部分 path-segment 匹配。)

所以,您可以这样写,而不是使用 <If> 表达式:

RewriteEngine On

# Get "id" (if any)
RewriteCond %{QUERY_STRING} (?:^|&)id=(\d+)($|&)
RewriteRule ^ - [E=ID:%1]

# Facebook|Twitter AND "id" param are passed
<If "%{HTTP_USER_AGENT} =~ /^(?i)(facebook(externalhit|platform)|twitterbot)/ && reqenv('ID') =~ /\d+/">
    # Get "lang" (default to "ja")
    RewriteRule ^ - [E=LANG:ja]
    RewriteCond %{QUERY_STRING} (?:^|&)lang=(\w+)($|&)
    RewriteRule ^ - [E=LANG:%1]

    # Redirects
    RewriteRule /news/detail\.html$ /api/v1/informations/ogp/%{ENV:ID}?lang=%{ENV:LANG} [R,L]
    RewriteRule /sport/detail\.html$ /api/v1/sports/ogp/%{ENV:ID}?lang=%{ENV:LANG} [R,L]
    RewriteRule /event/(common|special)/detail\.html$ /api/v1/events/ogp/%{ENV:ID}?lang=%{ENV:LANG} [R,L]
</If>