.htaccess RewriteCond and condition not working as expected - .htaccess

I'm trying to get a series of rewrite conditions working, with the logic being this
if condition1 or
(condition2 and condition3) or
..
This is what I have in the .htaccess:
RewriteCond %{HTTP_USER_AGENT} "iphone" [OR,NC]
RewriteCond %{HTTP_USER_AGENT} "android&mobile" [OR,NC]
RewriteCond %{HTTP_USER_AGENT} "iemobile" [NC]
Unfortunately, it looks like the and operator isn't working as I thought it would.
(as you might guess, the idea is to detect android phones but not tablets)
Is there a way to write that and condition to achieve the results I'm looking for?
Thanks.

Unfortunately, the [OR] flag doesn't work as nice enough for it to be useful, it only works for either all "or"'d or all "and"'d conditions. It's not very predictable. What you may need to do is separate them out to several rules and either use the S flag to skip stuff or the pass-through.
Maybe something along the lines of:
# Prevent rewrite looping
RewriteCond %{ENV:REDIRECT_STATUS} 200
RewriteRule ^ - [L]
# if "iphone" OR
RewriteCond %{HTTP_USER_AGENT} "iphone" [NC]
RewriteRule ^ - [S=3]
# "android" AND "mobile", OR
RewriteCond %{HTTP_USER_AGENT} "android" [NC]
RewriteCond %{HTTP_USER_AGENT} "mobile" [NC]
RewriteRule ^ - [S=2]
# "iemobile"
RewriteCond %{HTTP_USER_AGENT} "iemobile" [NC]
RewriteRule ^ - [S=1]
# skip everything, none of the conditions match
RewriteRule ^ - [L]
# apply the rule
RewriteRule ^ /do-something [L]
Looks like a mess but that's mod_rewrite for you.
The first rule is to prevent any sort of internal rewrite looping. The "# skip everything" rule is the one that gets applied if none of the 3 conditions match, it essentially does nothing excepts stops any rewriting. If you have other rules after all of this stuff that you want to get applied, you can replace the L flag with S=1.
The last rule is the rule that gets applied if any of the 3 conditions matches.

Related

.htaccess allow only user-agents that contain a specific word

I would like to know if there is a way to block all user-agents except the one that contains the word "chrome" using .htaccess
I used something like this, but this works unfortunately only if the exact name is given..
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} !Lynx/2\.8\.8dev\.12 [NC]
RewriteRule ^ - [F,L]
You can just use:
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} !chrome [NC]
RewriteRule ^ - [F]

Force redirect for certain files (based on referer) and trigger a 404 page otherwise

We distribute different versions of a software product through a single download link. The delivery is based on the referer in conjunction with a default value, which works fine. In addition the user should be redirected to a 404-page, in case the wrong filename was used.
At the moment the .htaccess-file looks like this:
# stop directory listing
Options -Indexes
# turn rewrite engine on
RewriteEngine On
# force 404 if file name is missing or wrong
RewriteCond %{REQUEST_URI} !^(download_mac\.zip|download_pc\.zip)$
RewriteRule (.*) 404/index.html [L]
# an example based on the referer
RewriteCond %{HTTP_REFERER} ^http://([^.]+\.)*domain-a\.com [OR]
RewriteCond %{HTTP_REFERER} ^http://([^.]+\.)*domain-b\.com
RewriteRule ^(download_mac\.zip|download_pc\.zip)$ domain_ab/$1 [L]
# last rule if no referer matches
RewriteRule ^(download_mac\.zip|download_pc\.zip)$ default/$1 [L]
So I have one issue and one additional question with this file:
The first rule, to force 404, is very greedy and gets the error page every time, no matter what URL is called. I also tried single statements like RewriteCond %{REQUEST_URI} !^download_mac\.zip$ without any effect. How can I fix this?
How can I get rid of the filenames in any other rule? I tried things like RewriteRule ^(.*)$ default/$1 [L] but it gives me a hard time and an 500 Internal Server Error.
You can avoid repeating your filenames by using an Env variable like this:
RewriteRule ^(download_mac\.zip|download_pc\.zip)$ - [E=ALLOWED:$1,NC]
RewriteCond %{ENV:ALLOWED} ^$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ /404/index.html [L]
RewriteCond %{ENV:ALLOWED} !^$
RewriteCond %{HTTP_REFERER} ^http://([^.]+\.)*domain-a\.com [OR]
RewriteCond %{HTTP_REFERER} ^http://([^.]+\.)*domain-b\.com
RewriteRule ^ /domain_ab/%{ENV:ALLOWED} [L]
RewriteCond %{ENV:ALLOWED} !^$
RewriteRule ^ /default/%{ENV:ALLOWED} [L]
You can just move the rewrite rule to the end. The other rules handle the valid cases and if none of them matches the last rule applies
# an example based on the referer
RewriteCond %{HTTP_REFERER} ^http://([^.]+\.)*domain-[ab]\.com
RewriteRule ^download_(mac|pc)\.zip$ domain_ab/$0 [L]
# last rule if no referer matches
RewriteRule ^download_(mac|pc)\.zip$ default/$0 [L]
# force 404 if file name is missing or wrong
RewriteRule ^ 404/index.html [L]

Is it possible to have nested ReWrite Conditions in htaccess?

I have the following situation where I want to have nested ReWrite Conditions, and I have come across a situation where I am not able to see a proper documentation for the same.
RewriteCond %{REQUEST_URI} ^(robots.txt|favicon|ico)$ [NC]
RewriteRule . - [S=3]
# Nested ReWrite Condition
RewriteCond %{HTTP_HOST} ^www
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI_1} [R=301,L]
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI_2} [R=301,L] # and so on
Therefore, the question comes up that whether the number of skip rules will comprise of the nested ReWrite Conditions, that is, in this case, should the number of skipped rewrite rules be 4 or 5(if including the rewrite condition).
RewriteCond %{REQUEST_URI} ^(robots.txt|favicon|ico)$ [NC]
RewriteCond %{HTTP_HOST} !^www
RewriteRule .* - [S=3]
# the following rules are run only if the first 2 conditions don't match
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI_1} [R=301,L]
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI_2} [R=301,L]
notice the ! negation in the 2nd cond
documentation:
This technique is useful because a RewriteCond only applies to the
RewriteRule immediately following it. Thus, if you want to make a
RewriteCond apply to several RewriteRules, one possible technique is
to negate those conditions and add a RewriteRule with a [Skip] flag.
Okay as you only have posted an example, I show you an example how it works. It's with comments, but if you still don't find it speaking enough, there is a lot more explanation available here.
# Does the file exist?
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Create an if-then-else construct by skipping 3 lines if we meant to
# go to the "else" stanza.
RewriteRule .? - [S=3]
# IF the file exists, then:
RewriteRule (.*\.gif) images.php?$1
RewriteRule (.*\.html) docs.php?$1
# Skip past the "else" stanza.
RewriteRule .? - [S=1]
# ELSE...
Rewri
This should solve your issue. If not, please update your example in the question so it's clear what you're missing.
And yes, it skips Rules and not Conditions.

ModRewrite in htaccess sends me to the full path

I have got those two rules:
RewriteCond %{HTTP_USER_AGENT} iPhone [NC]
RewriteRule ^categories$ home.php?categories=1[L,NC,PT,R=301]
RewriteRule ^featured$ home.php?featurez=1 [L,NC,PT,R=301]
The problem is that teh categories work and the featured doesnt work.
works:
http://apps.com/iphone/categories
doesnt work:
http://apps.com/iphone/featured
The second rule that doesnt work sends me to here
http://apps.com/var/www/vhosts/apps.com/httpdocs/iphone/home.php?featurez=1
It seems to send me the root of the root of my directory and that whole thing is prefixed with the root of my site..why?
How is that possible.
Rewrite conditions only apply to the immediately following rule, so your condition doesn't apply to the "featured" rule at all. You'll have to duplicate it.
Apache tries to guess whether the target of a rule is a URL-path or a file-path, and it's guessing incorrectly. You can try to fix it by either including a rewrite base or make your targets absolute URL-paths.
I've already answered this in your previous question using either of those solutions will fix the file-path appearing in the redirect.
RewriteBase /iphone/
RewriteCond %{HTTP_USER_AGENT} iPhone [NC]
RewriteRule ^categories$ home.php?categories=1[L,NC,PT,R=301]
RewriteCond %{HTTP_USER_AGENT} iPhone [NC]
RewriteRule ^featured$ home.php?featurez=1 [L,NC,PT,R=301]
or
RewriteCond %{HTTP_USER_AGENT} iPhone [NC]
RewriteRule ^categories$ /iphone/home.php?categories=1[L,NC,PT,R=301]
RewriteCond %{HTTP_USER_AGENT} iPhone [NC]
RewriteRule ^featured$ /iphone/home.php?featurez=1 [L,NC,PT,R=301]

how to generic .htaccess to prevent hotlink

The following code works fine:
RewriteCond %{HTTP_REFERER} !^http://superwebx.com/.*$ [NC]
RewriteRule .*\.(jpe?g|gif|bmp|png|swf|css)$ - [F]
but I want to make a generic script serve me for several sites I manage, but fails try to get
RewriteCond %{HTTP_REFERER} !^http://%{HTTP_HOST}/.*$ [NC]
RewriteRule .*\.(jpe?g|gif|bmp|png|swf|css)$ - [F]
You can't use variables inside the regex. You can work around this by using a RegEx backreference like so:
RewriteCond %{HTTP_REFERER} ^https?://([^/]+)/ [NC]
RewriteCond %1#%{HTTP_HOST} !^(.+)#\1$
RewriteRule \.(jpe?g|gif|bmp|png|swf|css)$ - [F]
(note the # is just used as a boundry. It could be any character that isn't used in domain-names.)
Very old one, but here's your answer:
RewriteCond %{HTTP_HOST}##%{HTTP_REFERER} !^([^#]*)##https?://\1/.*

Resources