2

The %{REQUEST_FILENAME} contains a string like this:

/web/webroot/index.php

I see a lot of people recommend testing if a file exists and has a .php extension like this:

RewriteCond %{REQUEST_FILENAME}.php -f

Isn't the .php after %{REQUEST_FILENAME} redundant? The variable should already contain the .php, right?

Seems like adding .php after %{REQUEST_FILENAME} would make the TestString check add an extra .php, like this:

/web/webroot/index.php.php

I'm missing something simple, I'm sure of it, but I just don't know what it is.

The context is this:

RewriteCond %{THE_REQUEST} ^.*?\.php [NC]
RewriteRule ^ - [R=404,NE,L]

RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^ %{REQUEST_FILENAME}.php [L]
Jeff
  • 1,406
  • 3
  • 26
  • 46

1 Answers1

2

Isn't the .php after %{REQUEST_FILENAME} redundant? The variable should already contain the .php, right?

Well, yes and no, it depends what you are trying to do.

RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^ %{REQUEST_FILENAME}.php [L]

This rule allows you to request extensionless URLs. eg. /index or /foo and this internally rewrites the request to /index.php and /foo.php respectively.

In this instance, REQUEST_FILENAME is a string of the form /web/webroot/index. So, it's testing that /web/webroot/index.php exists before rewriting to the same. If you were to request /index.php then the condition fails and no rewrite occurs (which is correct).

However, this rule isn't strictly correct and could cause a rewrite-loop under certain conditions. See the following related question:

MrWhite
  • 11,643
  • 4
  • 25
  • 40
  • 1
    I see you know your stuff. I'll stare at this for a few more hours until I figure it out. I think you're saying the variable is modified by appending the `.ext`, but I can't find this behavior documented anywhere except on SE forums. EDIT: Yeah, okay, I get it. Thanks! – Jeff Nov 27 '21 at 16:20
  • 1
    @Jeff The variable itself (ie. `REQUEST_FILENAME`) isn't modified, just the _TestString_ (first argument to the `RewriteCond` directive) - as you state in the question. It's basically testing whether the requested URL + `.php` exists as a physical file. Although, `REQUEST_FILENAME` isn't strictly the correct variable to use here (although works in _most_ cases), as mentioned in the linked question. (`REQUEST_FILENAME` is the "file" that the requested URL maps to.) – MrWhite Nov 27 '21 at 16:29
  • 1
    Well since I, of course, want to use strictly the correct variables in my code, is this the one: `%{DOCUMENT_ROOT}%{REQUEST_URI}.php -f`? – Jeff Nov 27 '21 at 16:50
  • 2
    @Jeff Yes, and change the `RewriteRule` _substitution_ string accordingly. ie. `RewriteRule ^ %{REQUEST_URI}.php [L]` – MrWhite Nov 27 '21 at 16:53
  • Awesome, thanks very much! – Jeff Nov 27 '21 at 16:58
  • @Jeff The regex `!\.\w{2,4}$` excludes all requests that already include what _looks like_ a file extension. It's just an optimisation, to prevent all static resources (images, CSS, JS, etc) from being unnecessarily tested to see if `image.jpg.php` exists etc. (filesystem checks are relatively expensive). Files don't _normally_ have two file extensions like `exists.qqq.php`, so wouldn't normally be a problem. But it is just an optimisation so is safe to remove. You could achieve a similar optimisation by excluding the subdirectory where all your static assets are stored. – MrWhite Nov 27 '21 at 18:09
  • Good stuff. Doesn't the `RewriteCond` line before the exclusion regex already exclude all those extra tests to the other static resources (besides .html files)? – Jeff Nov 27 '21 at 20:00
  • 1
    @Jeff The `RewriteRule` directive is processed first. Only when the `RewriteRule` _pattern_ matches are the preceding _conditions_ processed. If the `RewriteRule` _pattern_ is not successful then the entire rule is skipped and processing continues... – MrWhite Nov 27 '21 at 20:41
  • Wow, I did not know that. Thanks again! – Jeff Nov 27 '21 at 20:43