Apache htaccess multiple rewrite conditions for multiple rules

13,289
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.

Share:
13,289

Related videos on Youtube

Tom
Author by

Tom

Updated on September 18, 2022

Comments

  • Tom
    Tom almost 2 years

    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.)