SoFunction
Updated on 2025-03-10

Using X-SendFile header in PHP to make files download faster

Generally speaking, we can guide users to download files by directly pointing to a file located under Document Root.

However, if you do this, you can't do some statistics, permission checking, etc. So, many times, we use PHP to forward and provide users with file downloads.

Copy the codeThe code is as follows:

<?php
    $file = "/tmp/";
    header("Content-type: application/octet-stream");
    header('Content-Disposition: attachment; filename="' . basename($file) . '"');
    header("Content-Length: ". filesize($file));
    readfile($file);

But there is a problem with this, that is, if the file is in Chinese, some users may have garbled file names after downloading.

So, let's make some modifications:

Copy the codeThe code is as follows:

<?php
$file = "/tmp/Chinese name.";

    $filename = basename($file);

    header("Content-type: application/octet-stream");

//Processing Chinese file names
    $ua = $_SERVER["HTTP_USER_AGENT"];
    $encoded_filename = rawurlencode($filename);
    if (preg_match("/MSIE/", $ua)) {
     header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
    } else if (preg_match("/Firefox/", $ua)) {
     header("Content-Disposition: attachment; filename*=\"utf8''" . $filename . '"');
    } else {
     header('Content-Disposition: attachment; filename="' . $filename . '"');
    }

    header("Content-Length: ". filesize($file));
    readfile($file);

Well, it looks much better now, but there is another problem, that is, readfile. Although PHP's readfile tries to be as efficient as possible and does not occupy PHP's own memory, it actually still needs to use MMAP (if supported), or a fixed buffer to loop through the file and output directly.

When outputting, if it is Apache + PHP mod, it also needs to be sent to Apache's output buffer. It is finally sent to the user. For Nginx + fpm, if they are deployed separately, it will also bring additional network IO.

So, can we directly let the Webserver send files to the user without going through the PHP layer?

Today, I saw an interesting article: How I PHP: X-SendFile.

We can use Apache's module mod_xsendfile to let Apache send this file directly to the user:

Copy the codeThe code is as follows:

<?php
$file = "/tmp/Chinese name.";

    $filename = basename($file);

    header("Content-type: application/octet-stream");

//Processing Chinese file names
    $ua = $_SERVER["HTTP_USER_AGENT"];
    $encoded_filename = rawurlencode($filename);
    if (preg_match("/MSIE/", $ua)) {
     header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
    } else if (preg_match("/Firefox/", $ua)) {
     header("Content-Disposition: attachment; filename*=\"utf8''" . $filename . '"');
    } else {
     header('Content-Disposition: attachment; filename="' . $filename . '"');
    }

//Let Xsendfile send file
    header("X-Sendfile: $file");

The X-Sendfile header will be processed by Apache and send the response file directly to the Client.

Lighttpd and Nginx also have similar modules. If you are interested, you can search for them.