Force file download with nginx

  1. The code
  2. What it does

I have spend way too much time trying to figure out how to add a simple Content-Disposition response header for static files when the request URI contains a force_download GET parameter, except for hidden and PHP files. Sure I could pass the request to another (php) script, adjust the headers and back to nginx, but that doesn’t make sense as nginx already receives all required information and can do scripting. At last I built it and here it is for anyone’s sanity.

The idea is to use URLs with the special argument and it would download the file instead of viewing in the browser. The code below supports these combinations:

https://domain/file.txt?force_download
https://domain/get.php?force_download=file.txt

The code

# Force download for certain URLs with ?force_download
set $forceDownload "";
set $filename "";

if ($request_filename ~ /([^/]+)$) {
  set $filename $1;
}

if ($request_uri ~ [\?&]force_download(=(?<filename>[^&$]+))?) {
  set $forceDownload "attachment; filename=\"$filename\"";
}

# Block hidden files
if ($filename ~ ^\.) {
  set $forceDownload "";
}

# Allow backend override
if ($sent_http_content_disposition) {
  set $forceDownload $sent_http_content_disposition;
}

# Set the header
more_set_headers -s 200 "Content-Disposition: $forceDownload";

What it does

  • Extract the filename from the request path.
  • Build the correct string when the request URI contains the force_download element.
  • Extract custom filename from the force_download parameter.
  • Clear the string when a hidden file is requested. (security)
  • Reset the string when the header was already set by the backend, for example PHP.
  • Then set the Content-Disposition header only when the status code is 200 (ok). When $forceDownload is empty the header is removed from the response.

Add this to your virtual host to force file download with nginx and have fun!

Oh btw, you need the HttpHeadersMore 3rd party module. In Ubuntu just install the nginx-extras package with apt-get.

Changelog
2022-01-28 – Fixed ‘if’ statement
2015-01-11 – Replaced filename argument with inline regex
2014-07-13
– changed order; backend headers rule
– cleaned up regexp
– added $filename at start to prevent error
– added filename argument


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *