Many times users need to download files from the website. If the file is publicly available through a fixed link, then we only need to store the file in the directory under webroot. But in most cases, we need to control permissions, such as downloading PDF bills or downloading files in a network disk. At this time, we usually implement it with the help of script code, which will undoubtedly increase the burden on the server.
For example, the following code:
// User identity authentication, if verification fails, jump
authenticate();
// Get the file to be downloaded, if the file does not exist, jump
$file = determine_file();
// Read file content
$content=file_get_contents($file);
//Send appropriate HTTP headers
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header("Content-Length: ". filesize($file));
echo $content; // or readfile($file);
?>1. What are the problems with doing this?
Doing this means that our program needs to read the file content from the disk through a fixed buffer to the memory in a loop, then send it to the front-end web server, and finally reach the user. When the file to be downloaded is large, this method will consume a lot of memory and even cause the php process to time out or crash. Cache is also a headache, not to mention interruption and reconnection.
An ideal solution would be to have the PHP program perform logical judgments such as permission checks. After everything passes, let the front-end web server directly send the file to the user - a front-end like Nginx is better at handling static files. This way the php script will not be blocked by I/O.
2. What is X-Sendfile?
X-Sendfile is a mechanism that transfers file download requests from the back-end application to the front-end web server for processing. It can eliminate the pressure on the back-end program to both read files and process sending, thereby significantly improving server efficiency, especially when processing large files. In the case of file download.
X-Sendfile is implemented through a specific HTTP header: specify the address of a file in the X-Sendfile header to notify the front-end web server. When the web server detects this header sent by the backend, it will ignore other output from the backend and use its own components (including optimizations such as caching headers and breakpoint reconnection) to send the file to the user.
However, before using X-Sendfile, we must understand that this is not a standard feature and is disabled by most web servers by default. Different web servers have different implementations, including stipulating different X-Sendfile header formats. If configured incorrectly, users may download 0-byte files.
Using X-Sendfile will allow files in non-web directories (e.g. /root/) to be downloaded even if the file is not accessible under .htaccess protection.
Different web servers implement different HTTP headers sendfile header web server used
X-Sendfile Apache, Lighttpd v1.5, Cherokee
X-LIGHTTPD-send-file Lighttpd v1.4
X-Accel-Redirect Nginx, Cherokee
The disadvantage of using X-SendFile is that you lose control of the file transfer mechanism. For example, if you want to perform certain operations after completing the file download, such as allowing users to download the file only once, this X-Sendfile cannot do it because the background php script does not know whether the download was successful.
3. How to use?
Apache please refer to the mod_xsendfile module. Next I introduce the usage of Nginx.
Nginx supports this feature by default and does not need to load additional modules. It’s just that the implementation is a little different, the HTTP header that needs to be sent is X-Accel-Redirect. In addition, you need to make the following settings in the configuration file
location /protected/ {
internal;
root /some/path;
}internal means that this path can only be accessed within Nginx and cannot be accessed directly using a browser to prevent unauthorized downloads.
So PHP sends X-Accel-Redirect to Nginx:
$filePath = '/protected/iso.img';
header('Content-type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
//Let Xsendfile send files
header('X-Accel-Redirect: '.$filePath);
?>In this way, the user will download the file under the path /some/path/protected/iso.img.
If you want to send /some/path/iso.img file, then the Nginx configuration should be
location /protected/ {
internal;
alias /some/path/; # Note the last slash