I want to redirect url to my ogp page when User-agent matches Twitter or Facebook.
My redirect image is like this.
/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
So I wrote htaccess, Env params work ok, but rewrite rule does not work when RewriteRule takes two or more over.
<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>
I want to redirect them all with same RewriteCond, how to do that?
Even dirty code is welcome!
/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
This is really only 4 URL mappings (when the lang
param is omitted it assigned a default - the target remains the same), so you only need 4 rules (if you extract the lang
param). And the last 2 redirect to the same target so these can be easily combined (using regex alternation (common|special)
), leaving just 3 separate rules that are required.
You could do it like this to avoid any repetition:
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]
The trick here is the use of the S
(skip
) flag to skip over the following 3 rules when the User-Agent is not Facebook or Twitter (or there is no id
URL param present) - reversing the logic. Which means that the last 3 rules are only processed when the User-Agent is Facebook or Twitter and the id
URL param is present.
Note that I used non-capturing subpatterns to ensure that the backreferences (ie. %1
) only refer to the group we are interested in.
NB: I changed the RewriteBase
to "simplify" the last 3 rules. Although if you have other directives then you may want to change this back.
I tried the method of using the If directive, but it didn't work
Your current rules would have failed to match inside an <If>
directive (Apache 2.4), because in this context the RewriteRule
directive matches against the absolute file-path, not a relative URL-path (probably because <If>
containers are merged very late).
Providing there are no conflicts, a simple workaround here is to just remove the start-of-string anchor (^
) from the start of the RewriteRule
pattern so as to be able to match a file-path of the form /absolute/file/path/to/public_html/news/detail.php
. (Although I would probably add a slash prefix in order to avoid any partial path-segment matches.)
So, you could potentially write it like this instead using an <If>
expression:
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>