I'm getting a puzzling error when using XSendFile and mod_rewrite.
XSendFile works properly when serving a file from a PHP script without a rewritten URL, but breaks causing a 404 error when a URL rewrite is happening.
To recreate the issue I've made a test directory with a .htaccess, a php script, and a video file.
The .htaccess:
RewriteEngine On
XSendFile On
RewriteRule ^file.mp4$ file.php [QSA,L]
The PHP script (file.php):
<?php
$video = 'target_file.mp4';
header('X-Sendfile: ' . $video);
exit();
?>
These are in a directory /var/www/xsendfile_test/
When I load http://localhost/xsendfile_test/file.php, it works as expected and I get the video file.
When I load http://localhost/xsendfile_test/file.mp4, I get a 404 error.
(If I remove XSendFile and just send the video via PHP using readfile() it works with the redirect, so I know the .htaccess is working).
Here's the weird part: my assumption was that the redirect shouldn't affect XSendFile, because the redirect happens on the request to the server, and XSendFile only gets involved when the server is about to send headers back to the client.
But, looking at the Apache error log, I see this: [:error] [pid 10961] (2)No such file or directory: [client 127.0.0.1:58236] xsendfile: cannot open file: redirect:/xsendfile_test/target_file.mp4
Looks like XSendFile is noticing that there's a redirect going on, and failing to find the file.
In order to get an exact comparison between what XSendFile is doing with redirected vs non-redirected URLs, I changed the target file in the PHP script to a non-existent filename. This means I get an Apache error with both rewritten and non-rewritten URLs, to compare. Here's the result:
Non-rewritten URL (http://localhost/xsendfile_test/file.php):
[:error] [pid 10962] (2)No such file or directory: [client 127.0.0.1:38090] xsendfile: cannot open file: **/var/www/xsendfile_test/missing_target_file.mp4**
Rewritten URL (http://localhost/xsendfile_test/file.mp4):
[:error] [pid 10963] (2)No such file or directory: [client 127.0.0.1:56944] xsendfile: cannot open file: **redirect:/xsendfile_test/missing_target_file.mp4**
For some reason, when accessed via a non-redirected URL, XSendFile loads the file via the absolute path. When there's a redirect involved, it ends up with only the path from the server's web root directory, and breaks.
I've tried adding various combinations of path parts in the PHP script and in the htaccess, without success.
Any insight as to why the redirected URL is behaving differently, or what to do about it?
Side note on absolute paths and whitelisting:
One possible approach that I want to avoid is to set the absolute path in the PHP script, along with adding the path to the server's XSendFilePath whitelist via the Apache config.
For the actual application this is not a workable solution, because it would mean that any server the code runs on has to have the configuration modified to accommodate the specific path. If this turns out to be necessary I will probably need to drop XSendFile altogether and use a different approach for sending files.
According to XSendFile docs, a file that is at or below the working directory in the filesystem can be accessed without whitelisting by using relative paths, which is the approach I want to stay with if possible.
(That said, the XSendFIle author helpfully responded to a previous SO post saying that how XSendFile constructs relative paths leads to potentially unexpected results. So maybe this is a quixotic quest and I need to find a different solution altogether...)
Any insights into this issue would be most welcome. Thank you!