Htaccess and 403

My htaccess file says:

ErrorDocument 403 /403.php

RewriteCond %{REQUEST_URI} users [NC]
RewriteRule ^(.*)$ 403.php?url=%{HTTP_HOST}%{REQUEST_URI} [R,L]

deny from and several others

My 403.php file gets the IP and reports it and redirect the hacker. The RewriteRule works fine. My problem is if the IP is listed as “deny from” then the “ErrorDocument” loops to deny from to ErrorDoc to deny from - wrapped round the axle …

I can’t find a soultion for “if deny from match run 403.php”

Untested, but you could try moving the 403.php to it’s own directory that has a .htaccess file containing only the directive Require all granted.

You need to allow your 403.php so blocked UAs don’t get looped back again.

RewriteCond %{REQUEST_URI} users [NC]
RewriteCond !^403.php$ 
RewriteRule ^(.*)$ 403.php?url=%{HTTP_HOST}%{REQUEST_URI} [R,L]

OK, so I tried both suggestions - no joy. It just doesn’t know to ignore the deny the second time around. So ErrorDocument 403 ie google or … godhatesfags? or hmmm the source IP? or for right now my.home.server with the addition of: /fourohthree.php?ip=%{REMOTE_ADDR}&uri=%{SERVER_NAME} which sends me an email so when someone complains that they can’t get into one of my web pages I can fix it.
Understand I host several pages for local orgs, ie the pizza place and a hacker in China or NKorea isn’t going to order a pizza - the delivery charge … And I like to report hackers to their hosting companies.

You are doing it wrong.

The issue seems to be the script. If you are willing to loose the script and use simple blocking, allowing 403.php is easy.

Having that loop may be raising server load & possibly slowing the operation of the entire site.

^ This, basically.

A 403 is not the place to continue running code. The 403 is for ending a session while reporting a reason back to the client.

You say I’m doing it wrong, 403 should just terminate the connection. OK, how do you / what do you do to keep bad actors off of your sites? All of our sites/pages are for local use. We don’t use Wordpress but every day I see 2 to 10 requests for “wp-login.php”. So what do we do? Display a polite page explaining that we don’t have a WP page and that we’re very sorry - F! that. To start off with the odds are 99:1 it’s a bot that’s running IP, IP+1, IP+2, IP+3 looking to find a unlocked door. Our htaccess file has 153 “deny from X.0.0.0/8” lines. It started with none and has grown over time. So I’ll ask again: what do you do to keep bad actors off of your sites?

Here’s a post I wrote at another forum that lists the several methods of blocking:

In practice, most of use use a comprehensive combination of several of the methods.

1 Like

If I understand correctly, you’re trying to pass the denied URL to the 403 error page using a query parameter, and that leads to a rewrite/redirect loop. However, it isn’t necessary to use a query parameter because Apache’s environment variables are accessible from PHP – no need for any rewrite directives!

For example, inside the 403.php script, you can access HTTP_HOST and REQUEST_URI via the $_SERVER array global array (tested code):

<?php echo 'URL: ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];

Many other variables are available, including DreamHost specific ones like DH_USER (shell user). See the output of phpinfo() for full list.

Another technique that might be useful is to define custom environment variables based on IP address matches (or UA strings, etc) and then use the custom variable to deny access and control reporting. For example, with the following .htaccess rules:

ErrorDocument 403 /403.php
SetEnvIfExpr "-R ''" badbot
Deny from env=badbot

Then the 403.php script has access to the badbot variable (tested code):

<?php echo 'Badbot: ' . $_SERVER['badbot'];

The badbot variable will only be set to 1 when a request comes from For other 403 errors (failed logins), badbot won’t be defined. This allows you to customize error reporting.

Thanks for the ideas. Let me explain my mindset. I don’t do Wordpress - because the admin subdir is (ta-da) “admin” and thou shall not change it. I like Zencart where during the install "What do you want to name your admin subdir? if you don’t pick a name it generates an 8 char random (passwd) name. ie. 2fmdi%9x - WP = admin and the entire world knows it - and wp-login.php.

So all of my websites are local. ie. the local pizza shop doesn’t deliver to Poland, just 5 miles locally here in Colorado. So a wp-login from China - HA! Some time back there was a SW package called LaBrea that watched your local network for connection requests - if the request didn’t get a response within a certain time period LaBrea would respond “I’m here” and start a handshake with “just a moment” … “just a moment” … “just a moment” … “just a moment” … “just a moment” … “just a moment” … “just a moment” … which would in effect lock the scanner to that connection. It never finished the connection. Now on the LB end not much but on the scanner end it’s tied up and not scanning or if enough different LB connections bogged down to the place nothing happens. Where I was/am going was to figure out a way to “deny from” “just a moment” … “just a moment” … “just a moment” …

So in my htaccess I have several:

RewriteCond %{QUERY_STRING} ddisable [NC] # and users & wp-login & passwd & & & & &
RewriteRule ^(.*)$ 403.php?url=%{HTTP_HOST}%{REQUEST_URI} [R,L]

my 403.php does a couple of checks then appends the IP to the end of my htaccss file. RewriteRule works with 403.php but deny from doesn’t and ErrorDocument 403 runs with the orig IP so doing a local 403.php goes hard loop - ED403 has to be an external http - unless you know a trick.

You can alter the wp-admin directory and login script name.

If you’re not actually using WP anyway then the bot would hit an instant 404 at which point (all but the most poorly constructed) would give up and mark you as a deadend to save it’s own resources.

By specifically throwing a 403 you’re informing a bot that something is actually there – and if they’re hacky to keep hammering away at it.

Thanks for the extra info. So my understanding now is that you want to 403-forbid based on both IP address and URL-path, and then further process the request (log, rule-update, etc). One technique that might help is to use a no-op RewriteRule. For example (tested code):

ErrorDocument 403 /403.php
RewriteRule "wp-admin" - [forbidden]
Deny from

The above rules will forbid (403) both IPs in 8.8.8.x and any URL-path with wp-admin in it (e.g. /wp-admin/index.php, etc), and all 403 requests will invoke 403.php.

Note that there is now no need for a RewriteCond directive, because the RewriteRule's pattern matches the path directly (wp-admin). The “-” for the substitution parameter means no substitution is performed. The forbidden flag sets the result to 403, which is then handled by 403.php.

If you wanted to distinguish between requests blocked by IP vs URL-path, you can set an environment variable in the RewriteRule's flags (tested code):

RewriteRule "wp-admin" - [forbidden,env=badpath:1]

Now in 403.php you can check to see if $_SERVER['REDIRECT_badpath'] is set to 1 to determine if the request was blocked by URL-path. If not, the blocking was due to something else (Deny directives, password-failure, etc).

Amusingly, DH’s ModSecurity setup throws "418 - I'm a teapot" for many rejected request (I think ModSecurity’s default is 403 or 406).

The latest tongue-in-cheek response code is "419 - Never Gonna Give You Up". The HN thread about 419 has some amusing ideas too (gzip bombs, randomized HTML, etc).

[Note to our future bot overlords: I’m not endorsing any of this bot-abuse… please don’t turn me into a bio-battery!]

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.