How I PHP: X-SendFile
It’s time for a new article in the ‘How I PHP’ series, which are about methods I use, but aren’t commonly known. This time it is about an Apache module ‘mod_xsendfile’.
Normally when want to let a user download a file, you simply stick it in a dir under the document root and let Apache do the rest.
However in some cases that is not good enough. You might need to do some authenticate first or you need to lookup the actual file name. In that case you would use PHP, which would result in a script looking like this:
authenticate(); # authenticate and authorize, redirect/exit if failed $file = determine_file(); if (!file_exists($file)) trigger_error("File '$file' doesn't exist.", E_USER_ERROR); header("Content-type: application/octet-stream"); header('Content-Disposition: attachment; filename="' . basename($file) . '"'); header("Content-Length: ". filesize($file)); readfile($file);
This means PHP has to read in the file, which goes through the output buffer, is flushed to Apache and processed before send to client. In this small I didn’t specify any other headers Apache normally sends like last-modified. If I want to actually make the caching based on last-modified work, I need to check the if-modified-since request header, check the mtime of the file and send a 304 result header. (I’m to lazy right now to write a code example for that, sorry)
Wouldn’t it be nicer to tell Apache, please send that file, and be done with it. Well, you can. When you enable mod_xsendfile in Apache, you can send an X-SendFile header, which is processed by Apache.
authenticate(); # authenticate and authorize, redirect/exit if failed $file = determine_file(); header("X-Sendfile: $somefile"); header("Content-type: application/octet-stream"); header('Content-Disposition: attachment; filename="' . basename($file) . '"');
Note that this technique was copied from Lighttp, so if you’re using that it will also work.
07 Mar 2008 Arnold Daniels





Hi, would it be too much hassle for you to post some (memory usage, cpu usage and speed) benchmarks? Thanks in advance!
Hello,
I have been trying to install and get it working with no succees…
I have test.php:
$file = “video.mp4″;
header(”X-Sendfile: “. $file);
header(”content-type: video/mp4″);
and .htaccess file in same directory that contains:
XSendFile on
I’m using flowplayer (www.flowplayer.org) to read the mp4 file trough test.php and flowplayer gives an error “streamnotfound”.
If i use readfile($file) in php-script. it works…
apache2ctl -t -D DUMP_MODULES shows that xsendfile_module is installed.
Do you have any ideas what would be wrong?
I really really appreciate your help!
Is there a way to use xsendfile for displaying inline images? I want to display the image in a browser instead of showing the save as dialog box.
Hi Fujiko,
For a png, use: header(”Content-type: image/png″);
does someone has compiled this mod for apache 2.2 ?
I tried but I have bad errors….
Xorax:
what errors did you get when trying to install? remember that with apache2, the program to compile modules is
apxs2
instead of
apxs
also, if for whatever reason you need to manually create the config file (eg Ubuntu), the LoadModule line is:
LoadModule xsendfile_module /usr/lib/apache2/modules/mod_xsendfile.so (substitute for the correct path if necessary)
I’m also trying to get this to work but cannot get it to install on an Ubuntu Plesk server, it fails to compile.
The server is running Apache2, but if you do a locate search it only finds apxs and not apxs2 as pharrington suggests it should.
I really need to get this working so any ideas would really be appreciated
On http://packages.ubuntu.com you can find which package to install for a specific executable or other file.
http://packages.ubuntu.com/search?searchon=contents&keywords=apxs2&mode=exactfilename&suite=jaunty&arch=any
If you’re running PHP as module, install ‘apache2-prefork-dev’.
downloads got stalled at getting file information at least with IE6.0 (not tested with other IE releases)
suggested hack : Cache-Control: must-revalidate doesn’t work,
any help?
thanks
so one of the downfalls i’ve found with this method is that, unlike with the stock PHP header methods, when you have more code after the xsend file code, it doesn’t wait until the filesend is done before executing the code… with the first example it waits until the filesend is done, but not with xsend. this is particularly useful if you need to log the fact that the user had a successful download. if the download failed then the code below the download would just not execute, so the user could download again.
what i’m getting at– is there a way around this with xsendfile? i’m looking for a way to mark that a user has successfully downloaded a complete file in the database, so they can’t download again. as of now using the same code with the xsendfile modifications, it just always marks the file download as successful because of this.