29

Q1: How to chain these two conditions making them if BOTH A AND B, then proceed...
Q2: How to make them stick for all the rewriteRules below and not just the first rule?

RewriteCond %{REQUEST_URI} ^IMAGE-.*$      // if filename starts with IMG- and,
RewriteCond %{REQUEST_FILENAME} !-f        // if file does exist, then proceed:
RewriteRule Rule1
RewriteRule Rule2
RewriteRule Rule3

# -- END IF -- STOP HERE -- #
Sam
  • 15,254
  • 25
  • 90
  • 145
  • Can you put `[L]` flag on the last one? Haven't tested it, just thinking from memory... – alex Mar 14 '11 at 23:54

3 Answers3

50

Here are some tricks for making a RewriteCond stack apply to multiple RewriteRule's, sorted by increasing WTF's per minute. But this is configuration and not code, so those rules don't apply, right? :-)

1. Environment variable

When you have many RewriteCond's, storing their result in an environment variable and then testing against in every rule is more compact.

# Your RewriteCond stack.
RewriteCond %{REQUEST_URI} !^IMAGE-.*$ [OR]
RewriteCond %{REQUEST_FILENAME} -f
# Store environment variable.
RewriteRule ^ - [E=TRUE:YEP]
# Assert environment variable in remaining RewriteRule's.
RewriteCond %{ENV:TRUE} =YEP
RewriteRule Rule1
RewriteCond %{ENV:TRUE} =YEP
RewriteRule Rule2
RewriteCond %{ENV:TRUE} =YEP
RewriteRule Rule3

2. Skip flag

This one's a bit subtle. Using the [S] or [skip] flag you can cause your entire block of RewriteRule's to be skipped.

# Your RewriteCond stack.
RewriteCond %{REQUEST_URI} !^IMAGE-.*$ [OR]
RewriteCond %{REQUEST_FILENAME} -f
# If RewriteCond's match, skip the next RewriteRule.
RewriteRule ^ - [skip=1]
# Otherwise, this rule will match and the rest will be skipped.
RewriteRule ^ - [skip=3]
RewriteRule Rule1
RewriteRule Rule2
RewriteRule Rule3

This acts sort of like an if-statement with the RewriteCond's being the condition and RewriteRule's being the code block.

You get less duplication, but the tradeoff is the code is less clear, and you have to update [skip=N] every time you add or remove a rule from this set of N RewriteRule's.

<fun>

All right, if you're still reading, here you'll find two more solutions where the WTF's per minute reach and exceed a critical point. They're for amusement only, and you'll see why.

3. Skip flag without N

Yes, there is a way to use the [skip] flag without including N, the number of RewriteRule's you want to apply the RewriteCond stack to. That is... if you include a pair of RewriteCond's before each RewriteRule, and oh yeah, one more at the end.

# Your RewriteCond stack.
RewriteCond %{REQUEST_URI} !^IMAGE-.*$ [OR]
RewriteCond %{REQUEST_FILENAME} -f
# If RewriteCond's match, skip the next RewriteRule.
RewriteRule ^ - [skip=1]  # succeeded
RewriteRule ^ - [skip=2]  # failed
RewriteRule Rule1
RewriteRule ^ - [skip=1]  # succeeded
RewriteRule ^ - [skip=2]  # failed
RewriteRule Rule2
RewriteRule ^ - [skip=1]  # succeeded
RewriteRule ^ - [skip=2]  # failed
RewriteRule Rule3
RewriteRule ^ -           # no-op to cover for last [skip=2] rule

The trick here is that every [skip=1] rule gets processed if and only if the RewriteCond's succeeded, and every [skip=2] rule gets processed if and only if they failed.

4. URL marker

Use part of the URL to hold state then match against it in your RewriteRule's.

# Your RewriteCond stack.
RewriteCond %{REQUEST_URI} !^IMAGE-.*$ [OR]
RewriteCond %{REQUEST_FILENAME} -f
# If RewriteCond's match, prepend bogus marker "M#" to internal URL.
RewriteRule .* M#$0
# All your RewriteRule's test for this marker plus whatever else.
RewriteRule ^M#.*Rule1
RewriteRule ^M#.*Rule2
RewriteRule ^M#.*Rule3
# Finally, don't forget to strip off the bogus marker.
RewriteRule ^M#(.*) $1

The new URL with the marker is invalid, but the last RewriteRule revert it, right? Well, only if it gets processed, so don't let the marker URL escape this round of mod_rewrite processing before it gets reverted. You'll get a 404 then.

mxxk
  • 9,514
  • 5
  • 38
  • 46
  • 7
    I had to laugh loud in office, thanks for `sorted by increasing WTF's per minute`. Never have heard that before – PKeidel Jan 05 '17 at 12:54
27

Q1: The default is AND.

Q2: They will only affect the RewriteRule immediately below.

Clodoaldo Neto
  • 118,695
  • 26
  • 233
  • 260
16

You can't do it that way. You have to either repeat your RewriteConds or rethink your rewrite rule using references to do it all in one rule. The first route, though not pretty, would be the easiest. so

RewriteCond A
RewriteCond B
RewriteRule 1

RewriteCond A #duplicate
RewriteCond B #duplicate
RewriteRule 2
XP84
  • 1,236
  • 2
  • 11
  • 15
  • @user how about the comment of @alex on my question who says about adding [L] on the last one? – Sam Mar 15 '11 at 00:20
  • 1
    @Sam [L] just means "for this request don't do any more of these rules. Not really applicable to your situation. it's more for a situation where you say: if URL is like /blah/foo then do A [L] if URL is like /blah then do B. The [L] says stop here and don't do B even if it matches the 2nd condition. Hope that makes sense :) – XP84 Mar 15 '11 at 22:56
  • @ChuckKollars I thought of `[C]` too, but it depends on what the `RewriteRule`s are. Sometimes the `RewriteCond`s are the only real conditions, and the `RewriteRule`s always match (ex. `RewriteRule ^ - [E=foo:42]`). In cases like that where the rules will always match, `[C]` is the way to go. If, on the other hand, your `RewriteRule`s have conditions that may not match, you'll stop processing `[C]`s at the first rule that fails to match, which is probably not what OP wanted. – Dale Feb 23 '21 at 05:45
  • 1
    @ChuckKollars The syntax of `RewriteRule` is `RewriteRule pattern subst [flags]`, right? If, for example, you have three `RewriteRule`s with different `pattern`s, and you use `[C]` to chain them together, then the first `pattern` that doesn't match terminates the chain: any following chained rules will not run. My reading of OP was that they wanted three `RewriteRule`s to all be contingent on the preceding `RewriteCond`s, and I was just pointing out that `[C]` is the thing to use in this case *as long as* all of the rules' `pattern`s are guaranteed to match. – Dale Feb 25 '21 at 03:57
  • @dale I forgot that what works in my rather unusual environment often doesn't work in general; thanks for pointing this out! I've deleted my comments as they reflected a more specialized environment than the OP asked about. – Chuck Kollars Feb 26 '21 at 05:18