.htaccess rule to prompt file download

Here’s a quick .htaccess trick you can use to make a user’s browser prompt them to download an image rather than the image just opening in the browser.

RewriteEngine On
RewriteCond %{QUERY_STRING} =download
RewriteRule ([^/]*)$ - [L,E=download:$1]
Header onsuccess set Content-disposition "attachment; filename=%{download}e" env=download

What this does is that any links to images (or any other file) will be handled normally, but if you add ‘?download’ to the end of your link, the user will be prompted to download the file instead, e.g.

Normal link <a href="/filepath/some-image.jpg" >Link</a>
Download link <a href="/filepath/some-image.jpg?download" >Link</a>

Here’s an explanation of the rule:

  • RewriteEngine On We need to use a rewrite rule for checking the query string is ‘download’, so we need to make sure the RewriteEngine is switched on for this to work.

  • RewriteCond %{QUERY_STRING} =download We check the query string exactly matches ‘download’.

  • RewriteRule ([^/]*)$ The first part of the rewrite rule matches anything that is not a forward slash up to the end of the path, so this gives us the filename. e.g. for /path/to/some/file.jpg the match would be file.jpg. By wrapping the match in brackets we store the match in the variable $1.

  • - The dash that comes next means don’t rewrite the path. We are only using the rewrite condition and rule to check the query string contains ‘download’ and to capture the filename, we don’t want to actually rewrite anything.

  • [L,E=download:$1] The L flag means this is the last rewrite rule, the E flag is used to create an environment variable called ‘download’, and set its value to the filename, which we captured and stored in $1.

  • Header onsuccess To get the file download prompt we need to send a header. We only want to do this if the file was found okay and there weren’t any errors though, so we use onsuccess.

  • set Content-disposition "attachment; filename=%{download}e" The Content-disposition header is what makes the user’s browser prompt for download rather than displaying the file in the browser. We need to include the filename the file should be saved as, so we get this from the download environment variable we set in the rewrite rule.

  • env=download This means the Header will only be sent if the download environment variable is set. Since we only set the variable as part of our rewrite rule when the query string contains ‘download’, the header won’t be sent for any normal requests, only those where the query string is ‘download’.

Of course, you don’t have to use the query string ‘download’, you can use whatever you want, just amend the .htaccess rules accordingly.

Security concern

When using this rule, you must make sure that you either:

  • Place the .htaccess file containing the rule in a directory where you don’t mind users downloading any of the files
  • Or modify the rule so that the RewriteRule will only match the types of files that you don’t mind users downloading e.g. RewriteRule ([^/]*\.jpg)$ - [L,E=download:$1] will only match .jpg files.

Otherwise, if you placed this rule in an .htaccess file at the root of your site, a hacker could use the ?download query string to download any file at all. For example, on a wordpress website they could access /wp-config.php?download to download your wp-config.php file. They’d then have access to your database login details and could wreak havoc on your website.

So be sensible about what files you allow people to download. I use this rule on my wordpress site in the uploads directory. This only contains static files that I don’t mind people downloading.

Alternative method

An alternative to this method is using a scripting language, such as PHP to deal with file downloads. You would write a PHP script that takes the file as an input, and then sends the correct headers and the file content. However, using .htaccess instead means that your server doesn’t have to start up a PHP (or other scripting engine) process, saving time and memory, and leaving PHP (or whatever) to deal with your dynamic page requests.

If you think there’s anything wrong with the above, you have any suggestions for improvements, or comments, please leave a comment below.

Posted on by xoogu, last updated

Leave a Reply