MattDahEpic
MattDahEpic

Reputation: 73

I can't figure out why this RewriteCond isn't working

So I'm having trouble figuring out why my RewriteRules won't trigger. These rules are in an .htaccess file at the root directory of a subdomain of my website. I've turned on detailed logging for mod_rewrite in the VirtualHost but that isn't really helping me solve what's wrong, though the first three rules seem to be working simply by coincidence since their files exist at the requested location.

The goal of this set of rules is:
sub.domain.tld/ -> passthrough/serve actual file
sub.domain.tld/?q=test -> passthrough/serve actual file with query args intact
sub.domain.tld/.well-known/* -> passthrough/serve actual file (for letsencrypt)
sub.doamin.tld/* -> process.php?project=*
sub.domain.tld/*?q=test -> process.php?project=*&q=test while handling unlimited number of query args

And the current .htaccess is:

RewriteEngine on
#serve actual file if viewing main page or doing https renewal
RewriteCond %{REQUEST_URI} ^\?.+|\/$ [OR]
RewriteCond %{REQUEST_URI} ^\.well-known.*
RewriteRule (.*) - [L,QSA]
#redirect everything else to the processing script
RewriteCond %{REQUEST_URI} ^(\w+)
RewriteRule \/(\w+) process.php?project=$1 [NC,L,QSA]

Thank you for your help!

Upvotes: 0

Views: 533

Answers (2)

anubhava
anubhava

Reputation: 786091

You just need this single rule in your .htaccess:

RewriteEngine on

# skip files, directories and anything inside .well-known/ directory
RewriteRule ^(?!index\.|process\.php|\.well-known)(.+)$ process.php?project=$1 [L,QSA,NC]

Upvotes: 0

Capsule
Capsule

Reputation: 6159

OK, This was actually a complex one and because most of the time, %{REQUEST_URI} tests are done using the RewriteRule itself, I got a bit confused and I'm sorry about that.

It turns out:

  • %{REQUEST_URI} contains the leading slash
  • the matching part of the RewriteRule doesn't

Also, keep in mind %{REQUEST_URI} doesn't contain the query string, as stated in the Apache manual:

REQUEST_URI The path component of the requested URI, such as "/index.html". This notably excludes the query string which is available as its own variable named QUERY_STRING.

So, a rule like RewriteCond %{REQUEST_URI} ^\?.+ is pretty much useless as you'll never have a question mark in %{REQUEST_URI}

Also, and this probably is the most confusing part, when requesting /, %{REQUEST_URI} will contain the actual index file that has been served. So, if your DirectoryIndex is set to index.php index.html (in that order) and you have an index.html file in the root folder, {REQUEST_URI} will be index.html. If you have an index.php file, it will be index.php, but never /.

That being said, we can simply your rules to:

RewriteEngine on
RewriteCond %{REQUEST_URI} !^/(\.well-known|index\.php$)
RewriteRule (.+)    process.php?project=%{REQUEST_URI} [QSA]

Note that I added the $ inside the brackets to only match the end of string character after index\.php but not after \.well-known, so anything after \.well-known will also match.

You will need to replace index\.php with index\.html if you have an html index.

Finally, you don't need 2 rules for that. It's always better to have only one and exclude some URLs from it.

PS: you'll also notice you don't need to escape / as this is not considered as a regexp delimiter.

Upvotes: 1

Related Questions