Bug Report - Apache Config / FilesMatch / PHP

SYSTEM INFORMATION
OS type and version Debian 12
Webmin version 2.402
Virtualmin version 7.30.8 GPL
Webserver version Apache/2.4.62
Related packages PHP-FPM

So I just happened upon a bug within the Apache configuration for PHP.

Current Code:

<FilesMatch \.php$>
  SetHandler proxy:unix:/run/php/<domain-id>.sock|fcgi://127.0.0.1
</FilesMatch>

While this code works for executing PHP code file, the bug is that, when you attempt to use ErrorDocument or in my case FallbackResource within a .htaccess file, these directives will NOT work if you go to for instance:

https://mydomain.com/this-file-does-not-exist.php

Instead of getting your defined ErrorDocument URL, or FallbackResource URL, you’ll get a default 4xx or 5xx error with an error of type:

AH01071: Got error 'Primary script unknown'

In your error_log for the domain.

The fix is actually quite simple, though if a more elegant version of this fix makes more sense feel free to apply.

Here’s what I’ve tested, which works as intended:

<FilesMatch \.php$>
  <If "-f %{REQUEST_FILENAME}">
    SetHandler proxy:unix:/run/php/<domain-id>.sock|fcgi://127.0.0.1
  </If>
</FilesMatch>

The magic is wrapping the “SetHandler” in an If statement, which causes the file URI to only be parsed by PHP if it actually EXISTS. Otherwise, the “ErrorDocument” and/or “FallbackResource” (in my case) will get used from the .htaccess file as intended.

Cheers!

*** Reminder: the above code snippet applies to two sections for each domain, on port 80 and 443 respectively ***

Hello Peter,

What does your .htaccess file look like?

FallbackResource index.php

Apache was ignoring the directive for PHP files specifically.

If I went to say “does-not-exist.html” it’d work as intended, but if I went to “does-not-exist.php” it’d ignore the directive.

I traced as mentioned it back to the FilesMatch directive.

Why not just redirect to index.php?

First off I use FallbackResource in place of RewriteEngine.

*** I handle most of the logic that RewriteEngine does script side ***

Secondly ErrorDocument and FallbackResource are being ignored based on current structure which isn’t good, not to mention if any URI ending in PHP is immediately being sent to PHP without the file actually existing this is a waste of resources.

While testing to find a solution, I added ErrorDocument directive to the .htaccess file which also didn’t resolve things. After making the original noted adjustment things worked. It was a fluke that I discovered this issue, as after implementing the FallbackResource I accidentally navigated to an old URL where the file was removed, which exposed the issue.

The FilesMatch is fine and setup correctly, but the If statement simply makes sure the file exists before treating it as a PHP request getting rid of the error log entry and also allowing other directives to execute as intended.

I don’t think this is the right approach, let alone a more complicated one.

If the file doesn’t exist, we should log that either way. We shouldn’t mask it for all users in the default configuration, as I understand that’s what you’re asking for.

However, it’s not a problem to fix it locally in your .htaccess file, for example, like this:

# Enable RewriteEngine
RewriteEngine On

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

Doesn’t actually address the issue

FallbackResource is a legit alternative to RewriteEngine and ErrorDocument directive is also affected.

What’s the issue, though? Is it to prevent a request with a missing file from appearing in logs?

It prevents FallbackResource or ErrorDocument directive from being fired.

Unless there is a bug in my suggested solution, it allows maximum usage of Apache directives as user desires.

A non-existent file would then if defined be evaluated by FallbackResource, RewriteEngine or ErrorDocument directive as intended.

Right now it assumes if a .PHP extension exists, the file must exist and get evaluated by PHP binary which then produces an error preventing further directives to fire as I’m seeing it.

That’s the core of the issue.

Alright, I see now! So, adding the ProxyErrorOverride On directive to the Apache domain config, either before or after the <FilesMatch \.php$></FilesMatch> block, will solve your problem. This will give you the behavior you want.

Also, I strongly advise against using PHP files for error pages. A more reliable solution is to use simple HTML files.

I don’t use PHP for error handling per se, the ErrorDocument directive could have been a html file and it wouldn’t have rendered due to the upstream issue. I’ll test your suggestion shortly.

Yes, that’s right, I didn’t suggest it would.

It’ll work!

I do have one other thought, why not render my if statement along with your proxy one?

The reason being, why even attempt to proxy a non-existent file to PHP in the first place?

If the file does not exist, let Apache handle the error management for this case.

If on the other hand the file does exist, then also do the proxy override to address what could be an error with PHP itself.

Ugh,

I still want to add my “If” statement, however it doesn’t look like there is any configurable way to do so within Virtualmin. Doing a deep dive, everything related to the “FilesMatch” section for PHP-FPM is hardcoded in the “php-lib.pl” file. I’m not a perl developer, and cannot figure out what to change there so that the “If” statement is added between the “FilesMatch” and before the “SetHandler” directive.

I do insist that the “If” statement not only addresses directives being ignored in the sites “.htaccess” file, but also in fact reduces calls to PHP binary for files that simply DO NOT EXIST.

I guess until a SOLUTION can be found, I’ll have to manually adjust websites that require the desired functionality and consider this a BUG in Virtualmin logic.

Original suggestion came from:

https://stackoverflow.com/questions/27408285/how-to-make-apache-check-if-php-file-exists-before-passing-it-to-php-fpm

With reference from Apache’s Confluence Wiki:

Hey @Jamie, what do you think about it?

Can we reliability determine in the Apache config if a PHP file is usable by the FPM server? Seems like there is a risk in doing this check before PHP-FPM gets a chance to look at the file.

@Jamie,

My “If” statement says “hey does a file called ‘does-not-exist.php’ actually exist? if it does, let’s send it to PHP to be executed, if NOT then let’s treat this like any other request… send a 404…”

Because it doesn’t waste resources trying to outsource the request to PHP, it can then be handled by Apache’s internal processes without wasting PHP overhead.

Also as originally pointed out, sending it to PHP without the check causes directives like “FallbackResource” and “ErrorDocument” not even to be evaluated, which also triggers an internal error “Primary script unknown” (since the file doesn’t actually exist).

If like other configurations for Apache and/or PHP there was a means of adjusting the logic in Virtualmin instead of the logic being “baked” into the source code, then I’d be able to adjust this for my system while others could use your default model if it suites them fine.

Ok, I see. So we could technically support this, but it’s a little tricky because we’d need to support existing systems that are still using the old config format. And If blocks are hard to parse, since Webmin tries to resolve them to determine which directives are active.

I wonder if there’s another solution that doesn’t involve using an If block?

Yes, adding ProxyErrorOverride On somewhere on the VirtualHost.

Ok that sounds a lot simpler! @tpnsolutions would this work for you?