3

I took over a website and noticed the previous developer had multiple .htaccess rewrite rules, this is a short example:

RewriteCond %{REQUEST_URI} !^/[0-9]+\..+\.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/[0-9a-zA-Z_-]+$
RewriteCond %{REQUEST_URI} !^/\.well-known/pki-validation/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteRule ^(.*)/landing/(.*)/(.*)$ landing.php?page=$1&id=$2&mid=$3
RewriteCond %{REQUEST_URI} !^/[0-9]+\..+\.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/[0-9a-zA-Z_-]+$
RewriteCond %{REQUEST_URI} !^/\.well-known/pki-validation/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteRule ^(.*)/landing/(.*)$ landing.php?page=$1&id=$2
RewriteCond %{REQUEST_URI} !^/[0-9]+\..+\.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/[0-9a-zA-Z_-]+$
RewriteCond %{REQUEST_URI} !^/\.well-known/pki-validation/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteRule ^(.*)/landing$ landing.php?page=$1

The Rewrite Condition is always the same for all rules, is there a way to combine the Rewrite Rules so the conditions just have to be written once?

I have looked into the skipping flag but it has no backreference possible. As far as I see I do not need backreferences with those conditions, however, is skipping the best approach? Or is there a best practice on how to achieve this? (Someone suggested to me to use the QSA flag, however, upon research I don't think that has anything to do with what I want to accomplish.)

2 Answers 2

4
RewriteCond %{REQUEST_URI} !^/[0-9]+\..+\.cpaneldcv$
RewriteCond %{REQUEST_URI} !^/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/[0-9a-zA-Z_-]+$
RewriteCond %{REQUEST_URI} !^/\.well-known/pki-validation/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$

This messy block of conditions is injected automatically by cPanel before every RewriteRule directive when it auto-renews an SSL (Let's Encrypt?) security certificate. These conditions ensure that the validation file (required in order to renew the SSL cert) is accessible.

Unfortunately, I've not been able to get to the bottom of why cPanel does it this way - it can be a maintenance nightmare and I have encountered directives that have been broken by these conditions (admittedly the directives were less than perfect to begin with). (I've tried asking on the cPanel forums, but I've never had a good response.)

...is there a way to combine the Rewrite Rules so the conditions just have to be written once?

Yes. You can move these conditions to their own block at the start of the .htaccess file and reverse their meaning, so instead of only triggering the RewriteRule when a request does not match the pattern (using a negated regex). You can prevent further rewrites when a request does match the pattern. For example:

# BEGIN cPanel SSL CERT RENEWAL
RewriteCond %{REQUEST_URI} ^/[0-9]+\..+\.cpaneldcv$ [OR]
RewriteCond %{REQUEST_URI} ^/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$ [OR]
RewriteCond %{REQUEST_URI} ^/\.well-known/acme-challenge/[0-9a-zA-Z_-]+$ [OR]
RewriteCond %{REQUEST_URI} ^/\.well-known/pki-validation/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteRule ^ - [L]
# END cPanel SSL CERT RENEWAL

Note the removal of the ! (negation) prefix on the CondPattern and the additional OR flag on the first three conditions. The RewriteRule then prevents further mod_rewrite directives being processed should the request match.

(On Apache 2.4.8+ these directives can be moved entirely to the server config. With the help of RewriteOptions InheritDownBefore.)

Then, followed by just the RewriteRule directives you had before (although I've added the L flag):

RewriteRule ^(.*)/landing/(.*)/(.*)$ landing.php?page=$1&id=$2&mid=$3 [L]
RewriteRule ^(.*)/landing/(.*)$ landing.php?page=$1&id=$2 [L]
RewriteRule ^(.*)/landing$ landing.php?page=$1 [L]

You should probably be including L flags on those remaining rewrites.

You could also combine those 3 rewrites into a single directive if you wanted, but that would depend if your application handles empty URL parameters.

(Someone suggested to me to use the QSA flag, however, upon research I don't think that has anything to do with what I want to accomplish.)

Yes, that doesn't really have anything to do with this particular problem. The QSA (Query String Append) flag would allow you to merge any query string that was present on the request with the query string you are writing in the RewriteRule substitution.

0

Problem:

For example, you have such conditions and rules:

RewriteCond %{REQUEST_URI} ^(...COND_1...)$
RewriteCond %{REQUEST_URI} ^(...COND_2...)$
RewriteRule ^(...RULE_1_FROM...)$ (...RULE_1_TO...)

RewriteCond %{REQUEST_URI} ^(...COND_1...)$
RewriteCond %{REQUEST_URI} ^(...COND_2...)$
RewriteRule ^(...RULE_2_FROM...)$ (...RULE_2_TO...)

RewriteRule ^(...RULE_3_FROM...)$ (...RULE_3_TO...)

Condition #1 and #2 are the same and you need to apply rules #1 and #2 in these conditions. Rule #3 does not depend on conditions #1 and #2 and must be separated from these conditions.

Solution:

If you don't want to repeat condition #1 and condition #2 each time for each rule you should make 2 changes:

Change 1. Create opposite conditions from #1 and #2:

RewriteCond %{REQUEST_URI} !^(...COND_1...)$ [OR]
RewriteCond %{REQUEST_URI} !^(...COND_2...)$

(I added !s and [OR]s here).

Change 2. You have 2 rules connected to these conditions, so add this line after all the conditions: RewriteRule ^ - [S=2]. Number in square brackets is a number of rules that you need to apply in these conditions. If you have 3 lines of rules you should write RewriteRule ^ - [S=3] and so on.

Here is the full code:

RewriteCond %{REQUEST_URI} !^(...COND_1...)$ [OR]
RewriteCond %{REQUEST_URI} !^(...COND_2...)$
RewriteRule ^ - [S=2]
RewriteRule ^(...RULE_1_FROM...)$ (...RULE_1_TO...)
RewriteRule ^(...RULE_2_FROM...)$ (...RULE_2_TO...)

RewriteRule ^(...RULE_3_FROM...)$ (...RULE_3_TO...)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .