Redirect or deny site visitors based on IP using .htaccess

As a webmaster, there may be times when you want to deny one or more IP addresses or ranges from accessing your website. Be it comment spammers, page scrapers, or various other reasons. For that, you’re welcome to adopt the following code snippets. If you’ve found them helpful, or have something to share, please do so in the comments below.

Deny by IP

Start by looking for the following two lines in your .htaccess file

order allow,deny
allow from all

Add the following directives BETWEEN those lines. If you don’t have the above two lines, add them and the following lines between them.

# Throws a "403 Forbidden" for the matching IP ranges
# Matches 91.46.*.*
Deny from 91.46.
# Matches exactly
Deny from

Redirect by IP

Copy/paste the following code and add to your .htaccess file, changing the RewriteCond and RewriteRule lines to meet your needs. Read the comments.

# Permanently redirect based on IP rage
<IfModule mod_rewrite.c>
RewriteEngine On
# Try uncommenting this line if it doesn't take
# Options +FollowSymlinks
# The following line describe the IP-pattern to match
# If 1 octet is given, it will match the entire Class-A address
# If 2 octets are given, it will match the Class-B address
# if 3 octets are given, will match the Class-C address
# If 4 octets are given, will match the full address
# The NC directive means Not-Case-Sensitive. It's probably not required.
# Matches exactly
RewriteCond %{REMOTE_HOST} [NC,OR]
# Matches 10.2.*.*
# The trailing period is needed to prevent partial-octet matching
# (i.e. matching 10.200. or 10.20. instead of just 10.2.
RewriteCond %{REMOTE_HOST} 10.2. [NC,OR]
# OR statement must be on all but the last IP to match
# It means, literally, "match this rule, or..."
# Matches 10.5.6.*
RewriteCond %{REMOTE_HOST} 10.5.6. [NC]
# Next line format: RewriteRule <regex to match> <destination> <Flags>
# by default, the next line matches all URLs
# and permanently (301) redirects to the destination.
# Use R=302 instead for a temporary redirect.
# L means this is the last rule to process when this condition is met (recommended)
RewriteRule .* [R=301,L]

Thomas indicates that there should be a trailing backslash-period at the end of the 4th octet, if the octet is less than 3 digits. In testing, I haven’t found it necessary (and it behaves as expected), but readers are encouraged to try it both ways. If you find that one way works over another, feel free to share in the comments.

Here’s a simpler, albeit somewhat less flexible method. This doesn’t use the Apache rewrite module, nor pattern matching, and may or may not fit your needs.

# 301 (permanently) redirect requests for /bar to
301 redirect /bar
# Also redirect the web root
301 redirect /

Questions, comments, and feedback regarding these methods are welcome and apreciated. Have something to contribute, or have feedback about the above code? Please feel free to share in the comments below!


, ,

  1. #1 by NMI on April 17, 2011 - 8:10 pm

    Thanks for this tip. It is much appreciated.

    • #2 by Mike on April 17, 2011 - 10:25 pm

      You’re welcome!

  2. #3 by Thomas on April 20, 2011 - 7:53 am

    I won’t give you my e-mail address, but I shall thank you for supplying the working version of this command.
    I tried another one with the “SetEnvIf REMOTE_ADDR” within the syntax, but it did not perform.
    Your version rocks. Thank you one more time, it’s a usefull command – for me at least.

    • #4 by Mike on April 20, 2011 - 8:19 am

      I understand about the email address. I don’t mind at all.

      You’re most welcome for the post! I have a few more coming out with more .htaccess rules, and you’re welcome to check back for them.

      Thanks for the comment, and I’m glad you found this useful! :)

      • #5 by Thomas on April 20, 2011 - 8:55 am

        not only that – believe it or not, by testing it I have found a small mistake :))

        Hint: right now your syntax is missing one character and while the last number in the IP number specified is less than three-digit one, then it will block now groups of addresses – 10 at a time if it’s two-digit, up to a 100 at a time – if a one-digit number is specified.

        Do you know already? :) If not let me know – I’ll enlighten you :)

        P.S. if everybody is cheeting on an email addres why make it required? Just leave this field as a voluntary one, just to all of those who want a response notification.

        • #6 by Mike on April 20, 2011 - 10:20 pm

          Thanks for pointing out the mistake in IP matching. I don’t know why I didn’t catch it as I was writing the article up. It’s fixed now.

          • #7 by Thomas on April 21, 2011 - 3:44 am

            Sorry to insist, but in the 4-octet sample you are still missing the backslash at the last octet.
            Should be and not like it is now.
            Otherwise you’ll get matched those eleven:
            3 30 31 32 33 34 35 36 37 38 39
            and this is only if the number is 3 or hihger.
            If it’s 1 or 2 (and perhaps 0) you’d be getting also three-digit octets: 100 101 102 103 … 110 111 112 … 120 121 … … 198 199 :)

  3. #8 by NMI on April 21, 2011 - 8:42 am


    In your last comment you have:

    Should be and not like it is now.

    Aren’t those 2 sets of number the same? I am not seeing anything different.

    • #9 by Thomas on May 6, 2011 - 7:48 pm

      yes, ofcourse, but please forward all complaints towards the owner of this blog :)
      This happened ONLY because the comment script utilized on this blog does not accept the “backslash” characters, which were included in my post while I was writing it, but disappeared after it was posted on the board.
      The backslash character should appear at the end of each octet – if it is supposed to be an exact match. Otherwise you will match ALL numbers begening with a one- or two-digit number you specify for a particular octet.

      • #10 by Mike on May 6, 2011 - 8:11 pm

        Thanks for coming back and clearing that up for the sake of discussion :)

        Thanks for your feedback regarding the backslashes as well. Please try escaping your backslashes (using two backslashes instead of one) next time. I was able to get them to display correctly using that method on a test comment. I’ll take a look at why the backslashes are getting “eaten” at some point in the future. I believe it has to do with the natural parsing of strings within PHP, but it could just as well be a bug.

  4. #11 by NMI on May 6, 2011 - 8:29 pm

    So “Thomas”, how should the numbers be read/displayed? As Mike suggested, can you repost the comment in question using double backslashes? Thanks.