Redirect a range of IPs using RewriteCond - apache

Currently I am redirecting all users except for the IP 12.345.678.90 using:
RewriteEngine On
RewriteCond %{REQUEST_URI} !/maintenance$
RewriteCond %{REMOTE_HOST} !^12\.345\.678\.90
RewriteRule $ /maintenance [R=302,L]
What syntax would I use to allow a range? In my Allow list I have:
Allow from 123.45.678.90/28
Would it work if I just update the REMOTE_HOST line to:
RewriteCond %{REMOTE_HOST} !^12\.345\.678\.90/28

If you're using Apache HTTPD 2.4 or later, you can use expressions to match REMOTE_ADDR against a CIDR mask.
The short form looks like this:
RewriteCond expr "-R '192.168.1.0/24'"
The following longer form is also available, but the documentation suggests it is less efficient:
RewriteCond expr "%{REMOTE_ADDR} -ipmatch '192.168.1.0/24'"
That makes the full solution to your example something like this:
RewriteEngine On
RewriteCond %{REQUEST_URI} !/maintenance$
RewriteCond expr "! -R '12.345.678.90/28'"
RewriteRule $ /maintenance [R=302,L]

You probably want the %{REMOTE_ADDR} to match against, but you can't use CIDR notation as the %{REMOTE_ADDR} is literally the remote address and you can use a regular expression to try to match against it. So for 123.45.67.89/28, (123.45.67.80 - 123.45.67.95), you'd have to do something like this:
RewriteCond %{REMOTE_ADDR} !^123\.45\.67\.8[0-9]$
RewriteCond %{REMOTE_ADDR} !^123\.45\.67\.9[0-5]$

Although this is an old question, I find it still very relevant. An alternative that does allow CIDR notation is the following (example is in a virtualhost apache conf file):
<VirtualHost *:80>
.
.
.
<Files maintenance>
Require all denied
Require ip 12.345.678.90/28
</Files>
.
.
.
</VirtualHost>
As a sidenote, I suspect, without having done any testing or finding any evidence, that this method is "faster" than the RewriteCond expr "-R '192.168.1.0/24'" methods mentioned.
This is for the simple reason that at this high level there appears to be less computational steps involved.
N.B. a requester from an IP that is denied will see a "Permission denied" or "Forbidden" type response. You can make this prettier by adding in a custom 404 page that responding with a 200/OK (this way Google won't penalise your domain). The 200/OK has to be the first line of your custom 404 page. For example in PHP, the first line would read:
<?php header("Status: 200 OK"); ?>
You'd want to do this for a legit page you redirect to. Actual 404s should respond with 404 to keep us from ending up with a ton of useless search engine results down the road.

Try this in .htaccess. It's working.
RewriteCond %{REMOTE_ADDR} !^123\.456\.789\.[0-255]

I like to use the following which allows partial address matching. In Your virtualHost/htaccess file
SetEnvIf HOST "siteYouAreworkingON.com" ACCESS_CONTROL<br>
SetEnvIf Remote_Addr "list of full or partia ipadresses separated by |"<br>
RewriteCond %{ENV:ACCESS_CONTROL} 1<br>
RewriteRule .* http://gohere.instead [L,R]
Hope it helps.

Related

Looking for a RewriteBase equivalent using RewriteRule

I'm facing an Apache configuration issue which can be summarized like follows.
On a unique hosting system I have a lot of different test sites, each one in its own subdirectory, so they are accessible through an url like myhostname.fr/sitename.
Hence in the corresponding .htaccess, the common practice is to have a RewriteBase /sitename before any of the RewriteCond+RewriteRule sets, and it works fine.
Now for one of these sites (say in the specialsite subdirectory) I had to create a dedicated domain so the url looks like domainname.myhostname.fr.
Then for this site to work the .htaccess now needs RewriteBase / instead of RewriteBase /specialsite, and it works fine too.
Here is the trick: being not so familiar with Apache I decided to experiment and wanted to also keep allowed to access this site through the common url myhostname.fr/specialsite.
So I had to find a way to conditionally use one of the above RewriteBase, depending on which is the current url.
The first way I tried was to work like this:
<If "%(HTTP_HOST) =~ domainname\.myhostname\.fr">
RewriteBase /
</If>
<If "%(HTTP_HOST) =~ myhostname\.fr/specialsite">
RewriteBase /specialsite
</If>
But I got a HTTP 500 error, and I take much time to understand that the <If> directive is available as of Apache 2.4, while my hosting only offers Apache 1.3!
So (thanks to some other SO answers) I thinked to another way, which is to first do:
RewriteCond %{HTTP_HOST} domainname\.myhostname\.fr
RewriteRule ^ - [E=VirtualRewriteBase:/]
RewriteCond %{HTTP_HOST} myhostname\.fr/specialsite
RewriteRule ^ - [E=VirtualRewriteBase:/specialsite/]
Then prepend all further RewriteRule replacement with the given VirtualRewriteBase, like in this one:
RewriteRule ^ %{ENV:VirtualRewriteBase}index.php [L]
But while it works fine for the domain-access version, it gives me an HTTP 404 error for the subdirectory-access version.
So in order to watch at how the replacement applied I changed the above rule for:
RewriteRule ^ %{ENV:VirtualRewriteBase}index.php [R,L]
And I observed that the redirected url looked like this:
http://myhostname.fr/kunden/homepages/7/d265580839/htdocs/specialsite/index.php
where kunden/homepages/7/d265580839/htdocs/ is the full document-root of my hosting.
You can notice that the document-root has been inserted between the two parts of the original url.
Moreover, the result is exactly the same whatever I put in place of /specialsite/ in my VirtualRewriteBase!
So here is my main question: why and how does this happen?
Also I'm obviously interested to a possible alternative solution to achieve the double-access availibility.
But above all I would like to understand...
But while it works fine for the domain-access version, it gives me an
HTTP 404 error for the subdirectory-access version.
That's because your second condition is never matched. Indeed, HTTP_HOST only contains the... http host ! The /specialsite is part of the REQUEST_URI (or can also be matched in RewriteRule directly).
This code should work (anyway, i don't know if it would solve totally your problem, but that's a first step)
RewriteCond %{HTTP_HOST} ^domainname\.myhostname\.fr$ [NC]
RewriteRule ^ - [E=VirtualRewriteBase:/]
RewriteCond %{HTTP_HOST} ^myhostname\.fr$ [NC]
RewriteCond %{REQUEST_URI} ^/specialsite(?:/|$) [NC]
RewriteRule ^ - [E=VirtualRewriteBase:/specialsite/]

Apache dynamic whitelist

I am looking for a solution for dynamic whitelist, so I do not need to restart apache2 service. I've tried to do something like this:
order Deny,Allow
include conf/IPList.conf
Allow from all
But this solution didnt work for me correctly. I've tried also this, but im not sure if my whitelist.txt is correct. How should it looks like?
## WHITELIST IPS ##
RewriteMap ipslist txt:/path/to/whitelist.txt
RewriteCond %{REMOTE_ADDR} ^(.*)$
RewriteCond ${ipslist:%1|black} ^black$ [NC]
RewriteRule (.*) - [F]
Or maybe there is another, better way to make dynamic whitelist for Apache2 ?
Using a rewrite map is fine. There's a reverse way to do this that I've posted about here.
You can simplify the rules a little though:
RewriteMap ipslist txt:/path/to/whitelist.txt
RewriteCond ${ipslist:%{REMOTE_ADDR}|black} ^black$ [NC]
RewriteRule ^ - [F]
The whitelist.txt file needs to look like:
1.2.3.4 ok
2.3.4.5 ok
etc.
The "ok" can be anything, but you need something that the whitelisted IP address maps to, other than "black". The whitelist.txt file will be cached by apache and when you change it, apache will automatically reload and reparse the file. This way, you don't need to restart apache.

Apache Rewrite: image directory based on HTTP host

My software supports multiple domain names all pointed at the same directory on the server (a different database for each of course). So these domains...
www.example1.com
www.example2.com
www.example3.com
...all point to...
/public_html/
In the image directory...
/public_html/images/
I have directories that exactly match the host names for each website:
/public_html/images/www.example1.com/
/public_html/images/www.example2.com/
/public_html/images/www.example3.com/
I'm trying to get Apache to rewrite requests so that if you view the image directly and look at the address bar you only see the host name once.
So a request for...
http://www.example1.com/images/book.png
...is fetched by Apache at...
/public_html/images/www.example1.com/book.png
One of the things I've tried and have had success with in different circumstances is the following though it doesn't work in this situation:
RewriteRule ^[^/]*/images(.+) images/%{HTTP_HOST}/$1
Try adding the following to the .htaccess file in the root directory of your site (public_html)
RewriteEngine on
RewriteBase /
#prevent looping from internal redirects
RewriteCond %{ENV:REDIRECT_STATUS} !200
#only rewrite gif, jpg or png
RewriteRule ^(images)(/.+\.(gif|jpg|png))$ $1/%{HTTP_HOST}$2 [L,NC]
Your rule
RewriteRule ^[^/]*/images(.+) images/%{HTTP_HOST}/$1
did not work because you have a leading / before images. In .htaccess the leading / is removed, so the rule would never match.
Here's one of the things I've made for my high performance framework (see my bio).
I give you an advanced RewriteRule, I'm pretty sure you'll have enough material to finish:
Create static domains:
static.example1.com
static.example2.com
static.example3.com
Where all your images will be.
From now on, no more:
www.example1.com/images/www.example1.com/picture.jpg
www.example2.com/images/www.example2.com/picture.jpg
www.example3.com/images/www.example3.com/picture.jpg
but
static.example1.com/picture.jpg
static.example2.com/picture.jpg
static.example3.com/picture.jpg
Nice URLs uh?
Now create a vhost with all your static files:
<VirtualHost *>
ServerName static.example1.com
ServerAlias static.example2.com static.example3.com
</VirtualHost>
Set your document root to the base without the vhost name, so in your case:
DocumentRoot "/public_html/images"
And add this RewriteRule
RewriteCond %{HTTP_HOST} ^static\.([a-zA-Z0-9\-]+)\.com$
# Change the path, and add the request:
RewriteRule (.*) %{DOCUMENT_ROOT}/static.%1.com$1 [QSA,L]
So all in all:
<VirtualHost *>
ServerName static.example1.com
ServerAlias static.example2.com static.example3.com
RewriteCond %{HTTP_HOST} ^static\.([a-zA-Z0-9\-]+)\.com$
# Change the path, and add the request:
RewriteRule (.*) %{DOCUMENT_ROOT}/static.%1.com$1 [QSA,L]
</VirtualHost>
Ok that doesn't aswer exactly to your question so here's the short answer, but I don't like it because it doesn't help you to do a very (very) good job:
RewriteCond %{HTTP_HOST} ^www\.(example1|example2|example3)\.com$
# Change the path:
RewriteRule (.*)(\.(css|js|txt|htc|pdf|jpg|jpeg|gif|png|ico))$ %{DOCUMENT_ROOT}/www.%1.com$1$2 [QSA,L]
And if that's not enough:
Two hints:
If you're not in a hosted environment (= if it's your own server and you can modify the virtual hosts, not only the .htaccess files), try to use the RewriteLog directive: it helps you to track down such problems:
# Trace:
# (!) file gets big quickly, remove in prod environments:
RewriteLog "/web/logs/mywebsite.rewrite.log"
RewriteLogLevel 9
RewriteEngine On
My favorite tool to check for regexp:
http://www.quanetic.com/Regex (don't forget to choose ereg(POSIX) instead of preg(PCRE)!)
John,
I've just posted a separate Q on some of the challenges that you face. I would welcome your comments, but back to your challenge: one trick that you can use an environment variable to store your (preferably validated) host, for example:
RewriteCond %{HTTP_HOST} ^www\.(host1|host2|host3\.com
RewriteRule ^ - [E=HOST:%1]
You might also want to add [S] flags to implement if/then/else logic in your rules. And you can also use the HOST variable in following rule or condition strings (not regexp patterns) as %{ENV:HOST}.
You also need to take a clear look at a full phpinfo() report to understand whether you hosting service is using an mod_php or a mod_suPHP, ... interface and host it supports DNS multihoming. E.g my supplier sets up %{ENV:DOCUMENT_ROOT_REAL} which I need to use instead of %{DOCUMENT_ROOT} when examining file-space.
All of your URI "arrivals" at DOCROOT/ are of the form http://www.exampleX.com/images/book.png so if your .htaccess location is your DOCROOT then your base is /. So assuming the above ENV setting, these should work
RewriteBase /
RewriteCond %{HTTP_HOST} ^www\.(host1|host2|host3)\.com
RewriteRule ^ - [E=HOST:%1]
RewriteCond %{ENV:HOST}==%{REQUEST_URI} !^(.*?)==/image/\1
RewriteRule ^image/(.*) image/%{ENV:HOST}/$1 [L,NS]
The cond is a botch to stop the rewrite rule looping.
Generalised version
The above solution is an already generalised solution as you as for. Just replace the RewriteCond regexp with whatever pattern matches your own naming convention, and I agree that if it is (.*) then you may as well drop the first rule and replace %{ENV:HOST} by %{HTTP_HOST}. You need the RewriteCond guard to prevent the loop which results in a 500.

custom ban page for homebrew website

I understand how to ban an IPs address from my apache webserver using .htaccess:
order allow,deny
deny from 192.168.44.201
deny from 224.39.163.12
deny from 172.16.7.92
allow from all
I'd like to create a custom "You've been banned" page. How could I do this?
EDIT:
To clarify, I am not trying to create a custom 403 page, as these are used in other instances as well (i.e. failed basic authentication, etc). The closest I have come so far is:
rewritecond %{REMOTE_ADDR} ^127\.0\.0\.1$
RewriteRule !^banned$ /banned [NC,L]
but this produces an internal server error when the IP is matched, instead of sending the user to /banned
The other answers which suggest an ErrorDocument for the 403 code would be the usual way to do this. But since you want to show a different error page if the user is denied access based on IP (as opposed to other reasons), you can use mod_rewrite, as you suspected.
RewriteCond %{REMOTE_ADDR} =192.168.44.201 [OR]
RewriteCond %{REMOTE_ADDR} =224.39.163.12 [OR]
RewriteCond %{REMOTE_ADDR} =172.16.7.92
RewriteRule !^/banned.html /banned.html [L]
P.S. This should go in your virtual host configuration, not an .htaccess file, if at all possible. If you don't have access to the virtual host configuration file, you could put it in a .htaccess file, but remove the leading slash from the RewriteRule pattern (so !^/banned.html becomes !^banned.html).
You use an ErrorDocument directive. People that are denied access are sent a 403 header, so:
ErrorDocument 403 banned.html
will redirect banned people to banned.html
Edit: The other alternative is to drop the mod_access stuff altogether, and use an IP-based rewrite rule (as mentioned in the question). It should just be:
RewriteEngine on
RewriteCond %{REMOTE_ADDR} 192.168.44.201 [OR]
RewriteCond %{REMOTE_ADDR} 224.39.163.12 [OR]
RewriteCond %{REMOTE_ADDR} 172.16.7.92
RewrulteRule .* banned.html [L]
Edit 2: David beat me to an almost identical answer; I think his way is what you want
In your .htaccess file:
ErrorDocument 403 /banned.html
change /banned.html to whatever path/to/filename you want.

mod_rewrite based on ip

I'd like to implement mod_rewrite to put my site into maintenance. Basically all IP addresses except a handful we specify would be forwarded to a static html page.
Please can someone help with this rule. Also is there a way to turn this on and off easily without editing the htaccess file?
You can use the REMOTE_ADDR variable in a RewriteCond
RewriteCond %{REMOTE_ADDR} !^10\.0\.1\.1$
RewriteRule ^ /maintenance.html
Just change the condition to match the IPs you want, for more than one you can use ^(ip1|ip2|...|ipn)$.
About how to disable the maintenance mode without changing the .htaccess file I think that's not possible short of writing a program that would delete it or otherwise modify it, an easy one would be to rename it.
I'd like to slightly correct Vinko Vrsalovic's answer.
RewriteCond %{REMOTE_ADDR} !^10\.0\.1\.1$
RewriteRule ^ /maintenance.html
This rule result will be infinite loop and HTTP server error, because it will be executed on redirection page too. To make it work you should exclude redirection page from the rule. It can be done this way:
RewriteCond %{REMOTE_ADDR} !^10\.0\.1\.1$
RewriteCond %{REQUEST_URI} !/maintenance.html$ [NC]
RewriteRule .* /maintenance.html [R=302,L]
Small improvement to Alexander's answer, it's not necessary to use regular expression for the IP address.
RewriteCond %{REMOTE_ADDR} !=10.0.0.1
RewriteCond %{REQUEST_URI} !/maintenance.html$ [NC]
RewriteRule .* /maintenance.html [R=302,L]
you could enable this state and disable it via some admin interface that is able to write to .htaccess (e.g. permissions set to 755 or 777). it would just always find the .htaccess, insert those two lines at the beginning and on disabling maintenance it would delete those two lines, leaving the rest of the file untouched
Optional redirect only specific addresses
Late to the party, and just an add-on if somebody needs it the other way around.
With this approach, you redirect only specific addresses into maintenance then play with the aliases.
ServerName 10.0.1.1
ServerAlias 10.0.2.1
ServerAlias 10.0.3.1
RewriteEngine On
RewriteRule ^(.*)$ http://www.domainname.com/maintenance.html$1 [L,R=301]