Jonathan Polansky

A pretty good webpage.

Tying multiple Apache RewriteRules to a set of RewriteCond(s)

Recently I had a set of mod_rewrite RewriteRules that I wanted to only be evaluated if a set of RewriteConds were matched. This turned out to be a much more difficult task than I had anticipated.

Combining RewriteConds is no problem; this is supported with the default ‘AND’ behavior or the ‘OR’ CondPattern flag (http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#rewritecond). It becomes much more complicated when you want to run a set of RewriteRules once your RewriteConds have been matched.

In my situation, I wanted to match a set of RewriteConds and then evaluate a set of RewriteRules but stop at the RewriteRule which pattern matched. Stopping evaluation is easy with the ‘L’ Substitution flag. The hard part was getting mod_rewrite to continue evaluating RewriteRules after the first one failed. See, a rewrite rule is only tied to the RewriteConds directly before it. You can chain them with the ‘C’ Substitution flag but that is only evaluated if the previous RewriteRule pattern matched. In my situation, I wanted to keep evaluating if it *didn’t* match *and* all the RewriteConds were true.

The solution was derived from a Google search for “rewritecond and multiple rewriterule”, surprisingly enough. The first hit (http://www.webmasterworld.com/forum92/1854.htm) gave enough of an answer for me to see how it could be done with negating the RewriteConds and using the ‘skip’ Substitution flag to get my desired functionality. Something similar to the following was the final set of rules:

# We will negate all of the RewriteConds.  On no HTTP_HOST match we will skip
# the rules we want to run if the HTTP_HOST matches a domain
RewriteCond %{HTTP_HOST} !^vanity1\.domain\.com\.?(:80)?$ [NC]
RewriteCond %{HTTP_HOST} !^vanity2\.domain\.com\.?(:80)?$ [NC]
# The following RewriteRule has to skip the number of rules below it
RewriteRule .*    - [S=2]
# The following rules will be run only if HTTP_HOST matches one of the
# above domains
RewriteRule ^/+$  http://destination.domain.com/landing-page/ [L,R=301]
RewriteRule (.*)  http://destination.domain.com$1 [L,R=301]

This set of rules gives the desired affect that if the HTTP_HOST matches vanity1.domain.com or vanity2.domain.com then all of the RewriteRules we want to run are evaluated. If it doesn’t match any of the domains all of the RewriteRules are skipped.


Tagged as , + Categorized as Tech

3 Comments

  1. Hi Jon,

    This is a nice article, as its been a while since I’ve touched Apache and I was also looking for something similar and didn’t see the skip modifier in the documentation.

    Your skipping rule looks a lot like your 2nd rule (which is being skipped). Is this something you’ve implemented and does that work the way you want it to? I guess I’m curious when your second rule (http://destination.domain.com$1) would ever be called, if the skipping rule is the same. Thanks,

    vol7ron

  2. Hi vol7ron. You’re right the skipping rule is very similar to the 2nd rule. Basically it’s because once the domain name is matched, we wanted to perform an action if our target URL pattern was matched (^/+$). If the target URL pattern didn’t match we wanted to have a default rule, (.*) (again, only when the domain name is matched).

    I’ll break this whole thing down and explain it again since I don’t think I did a great job in the original post.

    The first 2 RewriteConds are checking to see if our request came in with one of the 2 target domain names. If they both evaluate to true (the domain name doesn’t match either of the 2 specified) then the skipping RewriteRule is called and the 2 RewriteRules below it are skipped completely and mod_rewrite processing continues below them.

    If either of the two target domain names match then we do not hit the skipping rule (as the RewriteConds fail) and we process the two RewriteRules at the bottom. The two RewriteRules can be broken down into one specific rule that performs an action when an URL pattern we are targeting is matched and a second RewriteRule which is a default catchall (again, for only when the domain names match).

    Hope that helps answer your question and explain what is going on.

  3. Well explained. A minor comment though: The trick is not just to negate the multiple RewriteCond, but also to swap (their implicit) logical AND into a (explicit) logical OR;
    or as in your example the other way around — i.e. I guess that you started off with explicit logical OR and removed it.

comment_type == "trackback" || $comment->comment_type == "pingback" || ereg("", $comment->comment_content) || ereg("", $comment->comment_content)) { ?>

Trackbacks & Pingbacks

  1. VOL7RON

    Hi Jon,

    This is a nice article, as its been a while since I’ve touched Apache and I was also looking for something similar and didn’t see the skip modifier in the documentation.

    Your skipping rule looks a lot like your 2nd rule (which is being skipped). Is this something you’ve implemented and does that work the way you want it to? I guess I’m curious when your second rule (http://destination.domain.com$1) would ever be called, if the skipping rule is the same. Thanks,

    vol7ron

  2. comment_type == "trackback" || $comment->comment_type == "pingback" || ereg("", $comment->comment_content) || ereg("", $comment->comment_content)) { ?>

    Trackbacks & Pingbacks

    1. jpolansky

      Hi vol7ron. You’re right the skipping rule is very similar to the 2nd rule. Basically it’s because once the domain name is matched, we wanted to perform an action if our target URL pattern was matched (^/+$). If the target URL pattern didn’t match we wanted to have a default rule, (.*) (again, only when the domain name is matched).

      I’ll break this whole thing down and explain it again since I don’t think I did a great job in the original post.

      The first 2 RewriteConds are checking to see if our request came in with one of the 2 target domain names. If they both evaluate to true (the domain name doesn’t match either of the 2 specified) then the skipping RewriteRule is called and the 2 RewriteRules below it are skipped completely and mod_rewrite processing continues below them.

      If either of the two target domain names match then we do not hit the skipping rule (as the RewriteConds fail) and we process the two RewriteRules at the bottom. The two RewriteRules can be broken down into one specific rule that performs an action when an URL pattern we are targeting is matched and a second RewriteRule which is a default catchall (again, for only when the domain names match).

      Hope that helps answer your question and explain what is going on.

    2. comment_type == "trackback" || $comment->comment_type == "pingback" || ereg("", $comment->comment_content) || ereg("", $comment->comment_content)) { ?>

      Trackbacks & Pingbacks

      1. widheg

        Well explained. A minor comment though: The trick is not just to negate the multiple RewriteCond, but also to swap (their implicit) logical AND into a (explicit) logical OR;
        or as in your example the other way around — i.e. I guess that you started off with explicit logical OR and removed it.

      Leave a Reply