apache – What do RewriteCond and RewriteRule mean in an .htaccess file?

Question:

I know they are used for numerous purposes, whether internal or external redirection, but I never understood what each one does, whenever I need something I have to resort to ready-made scripts because I don't know how to do it.

For example, in this code:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?params=$1 [NC]

It redirects everything to my index and whatever is after it as a parameter, but what would the RewriteCond %{REQUEST_FILENAME} !-f and RewriteCond %{REQUEST_FILENAME} !-d lines mean?

Answer:

RewriteCond

You must use the RewriteCond directive to add conditions for whether or not to apply the redirect.

For example:

# Redirect when we have a single parameter
RewriteCond %{SCRIPT_FILENAME} !-f 
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^([a-zA-Z0-9_-]+)/$ index.php?mod=$1

In this case, the !-f and !-d flags are determining that the redirect will only occur if there is no file or folder with the corresponding url .

Syntax

The command syntax is:

RewriteCond TestString CondPattern

Where CondPattern is a perl-compatible REGEXP , with some additions.

You can prefix the pattern with ! to reverse its effect. There are a few variations that allow you to use CondPattern without REGEXP as well:

'<CondPattern'
'>CondPattern'
'=CondPattern'

These last three treat the CondPattern literally as a string, and respectively compare the TestString with the literal value of the CondPattern

See some more conditions:

  • -d Take the result of TestString and check if it's an existing directory
  • -f Take the result of TestString and check if it's an existing file
  • -s Do the same as -f , but only consider non-empty files
  • -l Checks if TestString result is path to symbolic link
  • -x Checks if TestString result is a path with +x permission
  • -F Equivalent to -f , but do a test to see if the file is actually accessible by Apache. This implies making an extra internal requisition in the test, beware of overuse.
  • -U Same as -F , but tests by URL and not Path

All these tests can be denied with a ! at first.

Variables

For both RewriteCond and RewriteRule, there are some pre-populated variables that can be used delimited with %{ } . Here are some of the more common ones:

  • REMOTE_ADDR is the remote host IP

  • REQUEST_FILENAME is the full path of the file that fulfills the original request, based on the machine's filesystem , not hosting-related. Caution: if used in VirtualHost, it has the same effect as REQUEST_URI .

  • REQUEST_SCHEME usually returns http or https depending on the connection.

  • REQUEST_URI is one of the most used variables. It is the one with the request path, such as index.html . It does not include the query . Everything that comes from ? onwards is returned in QUERY_STRING.

  • THE_REQUEST is the complete request line, such as GET /index.html HTTP/1.1 . It does not include headers and is not "escaped", unlike the other variables.

RewriteRule

The RewriteRule directive rewrites the URL, and if it occurs more than once, it is applied in sequential file order.

Syntax

 RewriteRule Pattern Substitution [flags]

Pattern is basically a Perl-compatible RegEx. On the first occurrence, RegEx is applied to the URL after it is decoded ( % ). The following RegExes are applied to the output of the previous Rewrite.

Substitution is a string that can be merged with results (groups) extracted from RegEx, and also variables delimited by %{ } as exemplified above.

flags

Here are some of the most common flags:

  • L considers this to be the last Rewrite, not applying any more rules that modify the URL

  • R=code redirects the indicated code (ex: 301, 302)

  • NC (No-case) makes comparisons ignoring case

  • N redoes the chain of modifications, starting with the first one again

  • QSA (query string append) after the modifications made, apply the original query string to the end of the line (in other words, preserve what was after the ? in the original URI)

  • CO sets a cookie in the format CO=NAME:VAL:domain[:lifetime[:path[:secure[:httponly]]]]

The full description of the flags can be found in the documentation .

Example:

RewriteCond %{HTTP_HOST} ^www.(([a-z0-9_]+.)?exemplo.com.br)$ [NC]  
RewriteRule .? http://%1%{REQUEST_URI} [R=301,L,QSA]

In the example above we are checking if the domain has www, and removing it in the next line, keeping the rest of the URL.

The flag R=301 determines that it is a permanent redirect, and the L indicates that it is the last operation in this chain. NC indicates that the comparison will ignore case, and the QSA will make sure that if there is a query string ( ?nome=valor&... ), it will be kept in the redirect.

Describing the example given in the question

  • RewriteEngine On
    Activates the Rewrite module, which does URL rewriting.

  • RewriteCond %{REQUEST_FILENAME} !-f
    Determines that rewrite will only apply if ( ! ) there is no file ( -f ) with that name

  • RewriteCond %{REQUEST_FILENAME} !-d
    Determines that rewrite will only apply if there is no ( ! ) directory ( -d ) with that name

  • RewriteRule ^(.*)$ index.php?params=$1 [NC]
    Redirects user to index.php?params=$1 for any URL entered.
    Here, $1 is the 1st group of RegEx, determined by the ( ) s in the capture.
    The NC in this case is useless, nor would it need to be in Rewrite.

Scroll to Top