Sending files better: Apache mod_xsendfile and PHP

March 6, 2009 – 5:01 pm Tags: ,

I have previously written a quick post on making files downloadable through PHP scripts. The example in the post reads the file itself into a variable, and as pointed out in the comments, it’s not necessarily a very good idea especially if you deal with large files.

Recently at work, we needed a reliable way to send files to users’ browser, and I decided to take a look at mod_xsendfile, as suggested by Tom Graham.

It’s also supported by other web servers such as Lighttpd and nginx.

Benefits of xsendfile over other methods

The advantages of xsendfile come from dealing with bigger files. If you try reading a big file into memory for sending through PHP, or otherwise send one through PHP, you may hit various roadblocks:

  • You may hit the PHP memory limit
  • You may hit PHP’s max execution time limit

If you don’t encounter these two, you may find that sending the file through the PHP process is slower, or it may eat more memory from your server than if it was sent by Apache.

mod_xsendfile solves all these problems.

Setting up mod_xsendfile

Since mod_xsendfile is not a standard Apache module, you will need to compile and install it yourself. This may be a problem on some hosting providers who won’t let you do this / refuse to install it for you!

Installing the module is quite simple:

  • Download the module
  • Make sure you have apxs or apxs2 installed. On Debian style systems this is usually in apache2-prefork-dev or apache2-threaded-dev packages
  • apxs2 -cia mod_xsendfile.c
  • You may need to manually load the module by using the following in your Apache configuration:
    LoadModule xsendfile_module /path/to/modules/mod_xsendfile.so

You now have the module installed. To use it, you will need to add the following settings to your Apache configuration, or to a .htaccess file:

# enable xsendfile
XSendFile On

# enable sending files from parent dirs
XSendFileAllowAbove On

The second one is optional, but it allows a useful behavior. You may want to store your protected files under the web root, so to allow xsendfile to access the files, you will need to enable XSendFileAllowAbove – otherwise you will only be able to access files that are in the same directory or in child directories of the directory, where the parsing script is.

Sending files

Sending a file with xsendfile is very straightforward:

<?php
//We want to force a download box with the filename hello.txt
header('Content-Disposition: attachment;filename=hello.txt');
 
//File is located at /home/username/hello.txt
header('X-Sendfile: /home/username/hello.txt');

You could omit the first header, in which case the browser would not necessarily show a download file dialog. Also, the X-Sendfile header will not show up in the user’s browser, and they will never see the real location of the file they received.

You will not need to send a Content-Length header, as Apache will take care of that for you.

In closing

There are multiple ways to send and store files. I think using mod_xsendfile is a quite good approach, as it avoids the problems associated with sending the files in a PHP script. It also allows Apache to do it’s job – sending files to users – while still giving you the ability to, for example, perform authentication using a PHP script.

Support in other web servers

For information on how to use x-sendfile with Lighttpd, see this comment by Kawsar Saiyeed.

For information on using x-sendfile with nginx, see this comment by Alexey Shockov

Share this:
  1. 22 Responses to “Sending files better: Apache mod_xsendfile and PHP”

  2. So am I right in thinking that this will stop the execution of PHP, but apache will be able to continue the serving of the file?

    By Ciaran McNulty on Mar 6, 2009

  3. Yep, that’s correct. To be precise, the PHP code will send the header, and when the PHP script execution has ended (be it with exit; or through “normal” means), Apache will check if the X-Sendfile header exists and push the file to the client.

    By Jani Hartikainen on Mar 6, 2009

  4. For those that are running Lighttpd and PHP via mod_fastcgi, I believe x-sendfile is supported out of the box and you just need to enable it by setting allow-x-send-file to true.

    See http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModFastCGI for more info.

    If you run Lighttpd 1.5 you should be able to use x-sendfile from a proxy server (e.g. Apache) if the setting is enabled.

    To secure downloads you can use mod_secdownload. See http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModSecDownload

    By Kawsar Saiyeed on Mar 6, 2009

  5. I was curios to read this ever since you were talking about it on #zftalk. I would want to use xsend_file where I have control over the web server.

    Is X-Sendfile part of HTTP spec?

    By Sudheer on Mar 6, 2009

  6. This is a “special” header, which is only parsed by the mod_xsendfile module. So it is not a part of any official specs

    By Jani Hartikainen on Mar 6, 2009

  7. The same effect can be achieved using X-ACCEL-REDIRECT header of nginx server (http://blog.kovyrin.net/2006/11/01/nginx-x-accel-redirect-php-rails/). On systems with nginx as proxy it’s a better solution.

    By Alexey Shockov on Mar 7, 2009

  8. Will X-Sendfile header will work when using the REST API of the amazon s3? I tried it in PHP but adding a header doesn’t work. Perhaps among of you have tried making it work. Please share your techniques!

    By Paul Namuag on Sep 17, 2009

  9. Just wanted to say thanks for this, its very useful.

    By Nanda on May 13, 2010

  10. I love Apache for this very reason! :)

    By Sammy on Aug 30, 2010

  11. You could just use an online file transfer service like filesdirect – nothing to install, 128-bit SSL encryption, works with any OS…

    By Frank on Sep 22, 2010

  12. Can the end of serving file by apache be notified back to a php or any loggin feature ?

    By Thomas on Dec 19, 2011

  13. XSendFileAllowAbove was deprecated in version 0.10 in favor of XSendFilePath which can only be configured from the server config, virtual host, or directory according to https://tn123.org/mod_xsendfile/

    By Heckman on Sep 13, 2012

  14. thank you for the nice roundup, i have a little question:

    xsendfile checks if you have sendfile installed, if not it uses mmap.

    well… i wonder if anyone here has done any benchmarks of which method internally is faster… id like to know before i bother installing the sendfile tool;-)

    By Oliver Leitner on Apr 7, 2014

  1. 9 Trackback(s)

  2. Mar 6, 2009: PHP tip: How to make a file downloadable through your script | CodeUtopia
  3. Mar 6, 2009: Sending files better: Apache mod_xsendfile and PHP - PHP-update.co.uk
  4. Mar 8, 2009: Sending files with the Zend Framework — Tom Graham
  5. Apr 28, 2009: Sending files better: Apache mod_xsendfile and PHP | CodeUtopia
  6. Feb 11, 2010: DivX Pseudo Streaming - php.de
  7. Nov 28, 2010: eKini Web Developer Blog » Apache mod_xsendfile for my Django app
  8. Aug 20, 2011: How do I protect large file downloads through PHP and/or Apache? - Admins Goodies
  9. Oct 30, 2012: Fix for large file download problem? | Open Cart Know How
  10. Jul 15, 2013: Sending files better: Apache mod_xsendfile and PHP

Post a Comment

You can use some HTML (a, em, strong, etc.). If you want to post code, use <pre lang="PHP">code here</pre> (you can replace PHP with the language you are posting)