Softpanorama

May the source be with you, but remember the KISS principle ;-)
Contents Bulletin Scripting in shell and Perl Network troubleshooting History Humor

Apache mod rewrite

News Apache Security Recommended Books Recommended Links Flags
mod rewrite mod_security Apache .htaccess file Using deny directive in apache .htaccess Blocking bad referrers
RewriteBase directive Perl Regular expressions Tips Humor Etc

Introduction

Module mod-rewrite actually implements a simple production type expert system. It has some interesting features and supports Perl-style regular expressions.

It operates using set of variable that come from three sources:

To use any of the variables from that long list, you wrap the variable name in curly braces, and prefix with a percent sign. for example

%{SCRIPT_FILENAME} 

Module mod_rewrite is probably the most important, the most well known and the most successful Apache module. It can be called the Swiss Army knife of Apache modules. 

In addition to sophisticated rule-based URL-rewriting based on Perl-compatible regular expressions, mod_rewrite gives you the ability to keep query-string input safe, keep your site search-engine-friendly, make your URLs user-friendly, retain “old” URLs after a restructuring, and stop hot linking. 

Rules of the game

There are two types of rules the mode operates with two types of rules RewriteRule and RewriteCond. 

RewriteRule is fundamental, main type of rules. It can be prefixed by one or several RewriteCond rules.

The latter can't exist by themselves and set of  RewriteCond rules should always end with  RewriteRule

Chaining of RewriteCond rules by default is performed using AND logical condition.

But it can be overwritten by specified OR - The OR flag which allows you to combine rewrite conditions with a logical OR relationship as opposed to the default AND.

You can also specify case sensitive or case insensitive matching: NC - The No Case flag tells Apache to ignore the case of the string in the regex and is often necessary when writing a RewriteCond statement that matches the {HTTP_HOST} variable for a domain name, which is not case sensitive.

Behaviors of RewriteRule is controlled by so called flags. Among them

Due to the this complex chaining of rules you can create "forever" loops in mod rewrite rule sets. In this case it will execute the loop predetermined set of times (let's say 100) and then exit with code 500.

RewriteRule

Module mod_rewrite provides configuration directives that can provide a wide range of rewriting.

Left side of the rule operates with URL, the same value as %{REQUEST_URI%}.

For example, you might have recently replaced an older section of your website with a newer more up-to-date version, with a different URL. To automatically redirect any URL in the old structure to the index in the new location, you might use something similar to this:

RewriteEngine On
# Rewrite requests for the old directory to the new index
RewriteRule \,org/(.+)$ \.info/$1 [R,NC,L]
This rule tells Apache to respond to all requests to any file in the .org site with the file in the  .info/ site. The flags at the end specify to redirect to the new URL, ignore case when matching, and to stop processing any other rules that might match the requested URL See flags.

Same can be done for level two directories

RewriteEngine On
# Rewrite requests for the old directory to the new index
RewriteRule /News/(.+)$ /Bulletin/$1 [R,NC,L]

mod_rewrite can intercepts the request and translate it into the query string–appended PHP request, which is then parsed through the PHP engine. Another thing to keep in mind: if the preceding directory actually does exist and the RewriteRule shown here is active, Apache will still completely ignore the real path and proceed as planned with its URL rewriting.

In order to use mod_rewrite, you have to inform Apache that you want to enable the module in the given configuration zone — a .htaccess file, or a <Directory> section in http.conf.

RewriteEngine takes either On or Off as its value. The next key line is simply a regular expression used to match an incoming URL, and the new URL to use in its place:

# Rewrite /category/item/ to catalog.php?... 
RewriteRule ^(\w+)/(\w+)/?$ catalog.php?cat=$1&item=$2

The pattern looks for a two-level directory request, using the \w pattern to match only valid “word” characters for each directory. Then, it uses the values of each of the two directories as the category and item number for the PHP script.  PCRE syntax used and  $-prefixed backreferences refer to submatches in the original pattern.

Backreferences are a simple way to include a sub-match from the previous pattern directly in the substitution string. Looking again at the rule:

# Rewrite /category/item/ to catalog.php 
RewriteRule ^(\w+)/(\w+)/?$ catalog.php?cat=$1&item=$2

There are exactly two submatches defined in the regular expression matched against the URL: both of the “word”-character groupings, indicating directories. In the substition string, the backreferences $1 and $2 are used to automatically include the value of the first and second sub-matches, correspondingly.

Backreferences simply start counting at 1, which corresponds to the first submatch.

You can actually use it as a rudimentary type-checking layer. In some situations, you might be pulling a number from a query string variable, a page number or item number for example. If you want to make absolutely sure it’s a number you’re using, and not something potentially more evil—like a SQL injection attack—you have two options.

The first and most commonly used method is adding extra code to your PHP scripts to actually check if the $_GET parameter indeed holds a number. For example, you might have the URL:

http://www.mydomain.com/article.php?page=2 

URLs such as this are often found in paginated content, usually in page-navigation links. Somewhere in the code that loads the specified page of the article, you might see the following:

<?php 
if (isset($_GET[‘page’]) and is_numeric($_GET[‘page’])) { $page = $_GET[‘page’]; } else { $page = 1; } 
// And so on.... 
?> 

The previous code simply checks first for the existence of a specific $_GET value, in this case ‘page’, and then checks if the value passed is indeed a number—if not, a default value of one is assigned.

Such type-checking is a good idea, and helps shield your PHP scripts from nasty script kiddies who want to break your website. Unfortunately, it can become somewhat tedious requiring your PHP scripts to check whether or not the value was set, and if it was indeed a number. Using mod_rewrite, you can eliminate the type checking from your PHP code—all you have to do is ensure the pattern matches only numeric values:

# Ensure only numeric values for the page

RewriteRule ^article/([0-9]+)/? article.php?page=$1
RewriteRule ^article/[^0-9]+/? article.php?page=1
The first RewriteRule checks for any valid URLs in the form of /article/page/, and translates the given request into the call to the PHP script. The second RewriteRule is set up to catch all malformed URLs calling for an article, but giving an invalid value for the page number. Any such request is automatically assigned the first page in the article.

There are two things to note in the previous set of RewriteRules. First, both rules are processed in order; processing does not stop at the first rule, but continues through all rules given. Second, the redirection to the article.php script is handled internally—no HTTP redirect/moved status code is sent to the client at all. In order to alter these default behaviors, you can use a set of flags at the end of each rule.

RewriteRule Flags

mod_rewrite uses "flags" to give our rewrite conditions and rules additional features. We add flags at the end of a condition or rule using square brackets, and separate multiple flags with a comma. The main flags with which you'll need to be familiar are:

In order to control the ways that mod_rewrite handles and processes each RewriteRule, you have a set of 15 different flags that you can add following each rule. Flags are contained within brackets, and multiple flags can be provided in a comma-separated list—all within a common set of brackets.

For example:

# Rewrite /category/item/ to catalog.php
RewriteRule ^(\w+)/(\w+)/?$ catalog.php?cat=$1&item=$2 [NC,R,L]

nocase|NC Similar to the i flag in PHP’s PCRE syntax, the nocase flag makes the pattern case-insensitive. For example, the following two patterns are identical:

RewriteRule ^([A-za-z])/? catalog.php?cat=$1
RewriteRule ^([a-z])/? catalog.php?cat=$1 [NC]
redirect|R[=code]
Normally mod_rewrite uses an internal Apache redirect when rewriting URLs, and this redirection is invisible to the user. If instead you wanted to do a formal redirect, complete with HTTP status code, you can use the redirect or R flag.

By default, the redirect flag sends a HTTP 302 Moved Temporarily code, such as in the following rule:

RewriteRule ^article/(\d+)/? article.php?page=$1 [R]
If you wanted to change the status code to a HTTP 301 Moved Permanently, or any other status code between 300 and 400, you simply add the code to the end of the redirect flag:
RewriteRule ^article/(\d+)/? article.php?page=$1 [R=301]
forbidden|F
Similar to the redirect flag, using the forbidden flag immediately sends a HTTP 403 Forbidden response to the client. Whenever both a redirect flag and forbidden flag are present for the same rule, the forbidden flag will have priority (the redirect will be ignored, 403 sent).

gone|G Another HTTP status code control flag, gone, sends a response of HTTP 410 Gone, to inform the client the page no longer exits. When combined, the gone flag has precedence over the redirect flag, but not the forbidden flag.

last|L To stop the processing of RewriteRules after a successful match is found, you use the last flag. In the previous example dealing with processing article page numbers, the rule processing would still continue to the second rule (but not find a match), even if the first rule was a successful match. To stop the processing after the first rule matched, you could simply use the last flag: RewriteEngine On

# Ensure only numeric values for the page
RewriteRule ^article/([0-9]+)/? article.php?page=$1 [L]
RewriteRule ^article/[^0-9]+/? article.php?page=1 [L]
next|N
The next flag restarts the rewriting process at the beginning of the rule list. When the rules are re-processed, the URL matched is not the original request from the client, but the internally rewritten URL at the point where the next flag was used. For example:
RewriteRule ^article/(\d+)/? article.php?page=$1
RewriteRule ^catalog/(\w+)/? catalog.php?cat=$1 [N]
RewriteRule ^catalog/(\w+)/(\w+)/? catalog.php?cat=$1&item=$2
In this example, if a URL is called that matches the second rule, such as http://localhost/catalog/ stuff/, rewriting will stop processing at that point and automatically restart at the beginning of the rule set, using the new substituted URL, catalog.php?cat=stuff. The substituted URL is then itself filtered through the whole rule set.

Care should be taken when using the next flag, as infinite loops are possible.

chain|C If you wanted to treat a block of rules as a single related unit, similar to the try...catch syntax in PHP, you can use the chain flag. When using the chain flag, if a rule matches, the processing continues as expected and ignores the chain flag. If a rule does not match and a chain flag is present, all of the following rules that are chained together will be skipped as a whole block. For example, the following set of rules skips to the last rule if the first one does not match: RewriteRule ^article/(\d+)/? article.php?page=$1 [C] RewriteRule ^catalog/(\w+)/? catalog.php?cat=$1 [C] RewriteRule ^catalog/(\w+)/(\w+)/? catalog.php?cat=$1&item=$2 # Skip to this rule if any of the above don’t match RewriteRule ^article/[^\d]/? error.php

skip|S=number This flag allows you to skip the next number of rules when the current rule matches. While the behavior of the skip flag is similar to the chain flag, it behaves in exactly the opposite manner—skip jumps on a successful match; chain jumps on a failed match. type|T=mime-type If you want to override the MIME type sent for a document, you can use the type flag. For example, if you have a bunch of .xhtml files you want to serve up using a proper XHTML MIME type, instead of text/HTML, you could use the following:

RewriteRule ^.+\.xhtml$ - [T=application/xhtml+xml]
cookie|CO=name:value:domain[:lifetime[:path]]
The cookie flag, as you might infer, sets a cookie on the client’s browser. The three required fields — name, value, and domain—specify the name of the cookie, the value of the cookie, and the domain for which the cookie is set. The optional lifetime parameter represents the life of the cookie, in minutes.

The path parameter sets the path of the cookie. For example, if you wanted to set a cookie named prevvisit to signify a user has visited the home page before (and shouldn’t be bothered with splash screens), you could use this:

RewriteRule ^index\.php$ - [CO=prevvisit:true:domain.com] env|E=var:value 

The env flag allows you to set an environment variable var to the value provided. The value can also contain regular expression backreferences ($N and %N) for more complex variable control. Multiple environment variables can be set in one group of flags—they just need to be comma-separated like the rest of the flag list. qsappend|QSA In most situations when you’ll use mod_rewrite, anything provided as a query-string to the original URL is dropped when you perform a rewrite. If you want to preserve that information instead of dropping it, you can use the qsappend flag.

When using this flag, the rewrite engine simply takes the original query string and appends it to any new query string provided in the rewrite. For example:

RewriteRule ^index\.php$ index.php?foo=bar [QSA] 

This rule would change the index.php file, using the URL index.php?moo=cow, to be index.php?foo=bar&moo=cow. In the event that a query-string parameter in the original URL shares the same name as a parameter in the RewriteRule, the original value takes precedence.

noescape|NE When mod_rewrite performs its transformations, it goes to great lengths to make sure special characters in the rewritten URL are escaped before performing any internal or external redirection. For example:

 rewriterule ^somedir/? index.php?page="foo\%3b" [r] 
Given this rule, Apache would redirect the /somedir/ directory as requested, and the $_GET value for page will be a “safe” escaped value of foo%3b—mod_rewrite has escaped the \%3b value instead of replacing it with its semicolon substitute. To tell mod_rewrite to avoid any automatic escaping, you use the noescape flag:
RewriteRule ^somedir/? index.php?page=foo\%3b [R,NE] 

Given this rule, the new value of the page query-string parameter will be foo;.

passthrough|PT Use the passthrough flag when you want to combine mod_rewrite with other Apache modules that provide similar URL-handling functionality, such as mod_alias. For example, if you wanted to rewrite /foo to point to /bar, and then use mod_alias to translate /bar to /baz, you might try the following: RewriteRule ^/foo /bar Alias /bar /baz Unfortunately, because of the way Apache handles URIs internally, it would not work as written.

To make it work, add the passthrough flag:

RewriteRule ^/foo /bar [PT]
Alias /bar /baz

A general rule of thumb is to use passthrough if you are using more than one URL translating module to process a file.

nosubreq|NS
Use the nosubreq flag to force the rewrite engine to skip a rule if the request is actually an internal subrequest. When using PHP and Apache together, there are seldom situations when this flag is actually needed. There are, however, some CGI scripting instances where this flag comes into play. For more information, see the Apache manual section on RewriteRule: http://httpd.apache.org/docs-2.0/mod/mod_rewrite.html#rewriterule  .

proxy|P The proxy flag tells the rewrite engine to stop processing the rule-set immediately and force the request through the Apache proxy module, mod_proxy. With all this attention being given to RewriteRule, you might think that it’s the main force behind mod_rewrite. In reality, its best role is part of the dynamic duo that is RewriteRule and RewriteCond.

RewriteCond

The RewriteCond directive behaves much like  if () statement: it tests a string against a pattern or condition. If the input matches the pattern or string, the RewriteRule immediately following the RewriteCond directive is processed. The general format for RewriteCond is as follows:
RewriteCond TestString CondPattern
TestString is the string you are evaluating, and CondPattern is the regular expression or comparison value to check against. 

Variables RewriteCond is used with

The most common way that RewriteCond is used is to match the input string against a regular expression, similar to RewriteRule. However, when you use RewriteCond, you have access to large number of variables, so your input string can be more complex than a simple filename match. 

Variables come from three sources:

HTTP Header variables

The following are common  HTTP Header variables:

Web Server variables

The following are the Server variables:

Special (Apache specific)  variables

These are various Special (Apache specific)  variables:

API_VERSION

The version of the Apache module API (not the same as the Apache version number, but closely related); used mainly for module development (internally)

THE_REQUEST

Complete HTTP file request string, including method, file requested, and HTTP version used

REQUEST_URI

The resource requested in the HTTP request including string after ? if such present. %{REQUEST_URI} is that part of the URI which follows the domain up to but not including the ?  character of a query string.

And is the only Apache variable that a rewrite rule attempts to match. Apache changed regex engines when it changed versions, so Apache version 1 includes the leading slash in  %{REQUEST_URI} while Apache 2 does not! We can satisfy both versions by making the leading slash optional with the expression ^/? (? is the metacharacter for zero or one of the preceding character). So now we have:

REQUEST_FILENAME

Full local file system path for the item matching the HTTP request  See Use of REQUEST_FILENAME in Apache mod rewrite

IS_SUBREQ Whether or not the request being processed is an internal subrequest; the value will be “true” if the request is a subrequest, “false” if not

HTTPS A value of “on” indicates SSL/TLS is being used, “off” if not

To use any of the variables from that long list, you wrap the variable name in curly braces, and prefix with a percent sign, like so:

%{SCRIPT_FILENAME}

To see RewriteCond in action, first rewrite a RewriteRule to make use of RewriteCond. The old rule looked like this: # Rewrite /category/ to catalog.php RewriteRule ^(\w+)/?$ catalog.php?cat=$1&item=$2 To accomplish the same goal using RewriteCond in conjunction with RewriteRule, use the following:

# Rewrite /category/item/ to catalog.php
RewriteCond %{SCRIPT_FILENAME} ^(\w+)/(\w+)/?$
RewriteRule .* catalog.php?cat=$1&item=$2
Notice how the server variable SCRIPT_FILENAME was used as the test input string. Combining RewriteCond with RewriteRule using these server variables, you can come up with some interesting combinations. The following checks to see if the user’s browser can accept XHTML MIME types, and if so, changes the MIME type header sent for .html files:
RewriteCond %{HTTP_ACCEPT} application/xhtml\+xml
RewriteRule .*\.html$ - [T=application/xhtml+xml]

RewriteCond Flags

To help control RewriteCond further, a couple of flags are provided, similar to the RewriteRule flags.

nocase|NC Like the same-named flag used with RewriteRule, nocase specifies that the regular expression to be evaluated is case-insensitive.

|OR ornext The default chaining method for multiple RewriteCond directives is to use a logical AND. If the ornext flag is used, the two connected RewriteConds are compared with a logical OR. While the RewriteRule and RewriteCond directives deliver a majority of the power in mod_rewrite, there are a handful of other key directives that can help you control your rewriting and solve problems.

Chaining multiple lines together

Important feature of RewriteCond is the ability to chain multiple lines together. When listing multiple RewriteCond statements in a row, they are each treated like a programmatic AND—the RewriteRule at the end will only process if all the RewriteCond matches return true. The previous set of rules could be rewritten using multiple RewriteCond statements, like this:

RewriteCond %{HTTP_ACCEPT} application/xhtml\+xml
RewriteCond %{SCRIPT_FILENAME} \.html$
RewriteRule .* - [T=application/xhtml+xml]
In addition to the standard string-versus-regex comparisons, you can actually make simple comparison and system-check conditionals with RewriteCond. To compare your input string against another simple string, you can use the following as your conditional pattern: <CondPattern >CondPattern =CondPattern

Each of these checks is true if the input string is less than, greater than, or equal to, the CondPattern, respectively. For example, if you wanted to make a certain area of your website forbidden after a given year, you could use the following:

RewriteCond %{TIME_YEAR} >2005
RewriteRule .* - [F]
Along with these simple comparison operators, you have access to six more conditional checks that evaluate the statuses of files and directories: Comparison Operator Meaning

For example, to check to see if an image exists and actually contains some data, you could use the following:

RewriteCond %{REQUEST_FILENAME} \.jpg$
RewriteCond %{REQUEST_FILENAME} !-s
RewriteRule .* - [G]
The first condition checks to see if a .jpeg file is called, and the second condition actually checks to see if the file either doesn’t exist, or is 0 bytes. Notice the use of the exclamation point before the -s conditional pattern. Any conditional pattern used in RewriteCond can be prefixed with an exclamation point, thus negating it—identical to the use of the exclamation point in PHP conditionals.

RewriteBase

In most situations, your website URLs will not match your physical file system layout. The root of your website is almost never located at the root (/) of the local file system. In normal operation of Apache server, this is not usually a problem; when using RewriteRule, it can be very problematic. RewriteBase allows you to specify the base or prefix path for a set of URL rewrites. To fully understand the reasoning for RewriteBase, take a look at the following example ruleset used to rewrite a simple set of files:
RewriteEngine On
RewriteRule ^foo\.html$ bar.html [R]
Suppose your web root is being served out of the /www folder of the local file system (that is, your DocumentRoot is set to /www). If you tried to use the preceding rule in a per-directory access file (.htaccess), it would actually result in the request being rewritten incorrectly—instead of your expected
http://www.domain.com/bar.html file being returned, you get 
http://www.domain.com/www/bar.html.
Why is this? Here is a simplified version of what is happening internally to Apache:
Request: /www/foo.html (local physical path)
Rewriting:
/www/foo.html -> foo.html (directory prefix stripped)
foo.html -> bar.html (RewriteRule applied)
bar.html -> /www/bar.html (directory prefix re-applied)
/www/bar.html -> http://www.domain.com/www/bar.html
(domain prefix applied to URL, sent to browser)
To solve this, you simply add a RewriteBase statement to your ruleset:
RewriteEngine On
RewriteBase /
RewriteRule ^foo\.html$ bar.html [R]
RewriteLog
In order to get a first-hand glimpse of what is actually going on with mod_rewrite, try out the RewriteLog directive. You can use RewriteLog to specify a log file where a running record of the internal rewrite processing will be sent.

To enable a log of the rewrites alongside the other default Apache log files, use the following: RewriteLog “logs/rewrite_log” When specifying the path for the rewrite log, you can use both absolute and relative paths. If a relative path is used, it will be taken relative to the ServerRoot setting in httpd.conf. Note that the RewriteLog directive is applied on a per-server basis, so it must be placed in either the server config or a virtual host container inside httpd.conf—it is not allowed inside a <Directory> section or .htaccess file.

RewriteLogLevel -- Important for Debugging

To control how verbose the rewrite log records are, you can use the RewriteLogLevel directive. Given a number 0 through 9, with 9 being the most verbose, you can control how much internal processing is recorded. A setting of 0 disables logging altogether, and anything greater than 2 should be used only for debugging—it can slow down Apache on the higher settings.

 If you’d like to delve deeper into the workings of mod_rewrite, check out the mod_rewrite section of the Apache online manual: http://httpd.apache.org/docs-2.0/mod/mod_rewrite.html


Top Visited
Switchboard
Latest
Past week
Past month

NEWS CONTENTS

Old News ;-)

[Oct 21, 2017] Apache2 mod_rewrite and %{REQUEST_FILENAME} - Sysadmandine

February 23, 2010 admin
Notable quotes:
"... I must admit I read the description for REQUEST_FILENAME in apache2.2 several times before noticing that it was just the answer too used to read too fast! Thanks to this old post that made me re-read slower ! ..."
Oct 21, 2017 | amandine.aupetit.info

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^/(.*)$ /index.php?rt=$1 [L,QSA]

This means : if the requested file is not a real file, and isn't a directory, and isn't a symlink, then redirect to index.php.

I was really surprised to discover that it doesn't work. Though, everybody seems to use this syntax ! I checked my apache version : Apache/2.2.9 (Debian), nothing special with this one I guess.
To understand what Apache was doing with my rewrites, I activated the rewrite log :

RewriteLog /var/log/apache2/rewrite.log
RewriteLogLevel


Here's what I got (the interesting part, cause I got a looot more !) :

[blah blah blah] (2) init rewrite engine with requested uri /toto.htm
[blah blah blah] (3) applying pattern '^/(.*)$' to uri '/toto.htm'
[blah blah blah] (4) RewriteCond: input='/toto.htm' pattern='!-f' =&gt; matched
[blah blah blah] (4) RewriteCond: input='/toto.htm' pattern='!-d' =&gt; matched
[blah blah blah] (4) RewriteCond: input='/toto.htm' pattern='!-l' =&gt; matched
[blah blah blah] (2) rewrite '/toto.htm' -&gt; '/index.php?rt=toto.htm'

So apaches verifies only '/toto.htm' and not the whole path for "%{REQUEST_FILENAME}"? I thought though it was the whole path let's verify in the doc.
From http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html , by habit (cause I used apache 2.0 a lot more than apache 2.2 from now on) :

REQUEST_FILENAME : The full local filesystem path to the file or script matching the request.

Hmm. But I use apache version 2.2, so what do they say here http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html :

REQUEST_FILENAME : The full local filesystem path to the file or script matching the request, if this has already been determined by the server at the time REQUEST_FILENAME is referenced. Otherwise, such as when used in virtual host context, the same value as REQUEST_URI.

Ow.

REQUEST_URI : The resource requested in the HTTP request line. (In the example above, this would be "/index.html".)

Ok, I understand, I use virtual hosts (like everybody, uh?), so the real syntax for my needs is :

RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-l
RewriteRule ^/(.*)$ /index.php?rt=$1 [L,QSA]

This works even if it doubles the "/" between each variable (one / at the end of DOCUMENT_ROOT, and another at the beginning of REQUEST_FILENAME).

Here's the rewrite log showing that it works :

[blah blah blah] (2) init rewrite engine with requested uri /toto.htm
[blah blah blah] (3) applying pattern '^/(.*)$' to uri '/toto.htm'
[blah blah blah] (4) RewriteCond: input='/path/to/documentroot//toto.htm' pattern='!-f' =&gt; not-matched
[blah blah blah] (1) pass through /toto.htm

Now I can disable this log if I want to keep space on my disk.

I must admit I read the description for REQUEST_FILENAME in apache2.2 several times before noticing that it was just the answer too used to read too fast! Thanks to this old post that made me re-read slower ! 😉

Blocking unwanted visitors

#
 18 #order allow,deny
 19 #allow from all
 20 #
 21
 22 deny from 221.144.12.141
 23 deny from 219.94.162.14
 24 deny from 69.16.231.183
 25 deny from 67.201.60.20
 26 deny from 74.63.197.215
 27 deny from 205.251.128.71
 28
 29 RewriteCond %{HTTP_USER_AGENT} libwww.* [OR]
 30 RewriteCond %{HTTP_USER_AGENT} MaMa.* [OR]
 31 RewriteCond %{HTTP_USER_AGENT} MLBot.*
 32 RewriteRule ^.+ - [F]
 33
 34 RewriteCond %{QUERY_STRING} !^$
 35 RewriteRule .+ - [F]
 36
 37 RewriteCond %{REQUEST_URI} \.shtml/. [OR]
 38 RewriteCond %{REQUEST_URI} \.(php|asp|dll)[/?%]$)
 39 RewriteRule ^.+ - [F]
 40
 41 RewriteCond %{HTTP_REFERER} !^http://softpanorama.net/.*$      [NC]
 42 RewriteCond %{HTTP_REFERER} !^http://softpanorama.org/.*$      [NC]
 43 RewriteCond %{HTTP_REFERER} !^http://www.softpanorama.net/.*$      [NC]
 44 RewriteCond %{HTTP_REFERER} !^http://www.softpanorama.org/.*$      [NC]
 45 RewriteRule .+\.(jpg|jpeg|gif|png|bmp)$ http://www.softpanorama.org/Images/i
    nternet.gif [R,NC]