Force file download instead of opening in browser Using HTTP Header and Flask


Brief introduction of HTTP Header:

HTTP header fields are used between browser and web server for communication. It specify configurations and cookies that lay a foundation for the modern Internet.

An example of Request Header field: "user-agent" (what Operation System and Browser Version the client is using).

user-agent: Mozilla/5.0 (Linux; Android 6.0.1; SM-G532G Build/MMB29T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.83 Mobile Safari/537.36
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/601.7.7 (KHTML, like Gecko) Version/9.1.2 Safari/601.7.7

An example of Response Header field: "cache-control" (The cache policy).

cache-control: private, max-age=0
cache-control: public, max-age=30672000

The story of Content-Disposition:

There are 2 main methods for modern browser such as Chrome and Firefox to handle HTTP Response, show the content inline in the browser (open in browser), or download it as an attachment and saved at your laptop's local disk. In order to tell how browser showed deal with the data, web server needs to add "Content-Disposition" field in the HTTP response header.

Content Disposition header

if "Content-Disposition" field is not specified, "inline" will be used as default (open in browser), which is equal to:

Content-Disposition: inline

To force file download, specify "Content-Disposition" HTTP Header field as below:

Content-Disposition: attachment

To rewrite the attachment's name, specify filename within "Content-Disposition". if filename is not specified, the original file name will be used.

Content-Disposition: attachment; filename="test.txt"
force file download

Add Content-Disposition HTTP Header in Flask

If you are using Flask as your website project's framework, It's easy to set up any HTTP Header field. You can set a rang of URL with '/download/' prefix. Then add Content-Disposition HTTP header to all HTTP Responses!

def after_request(response):
    if str(request.path).startswith('/download/'):
        response.headers['Content-Disposition'] = 'attachment'
    return response

HTML5 Attribute Alternative

There is a new attribute in HTML5 specification, by adding download tag to any link to achieve force download.

<a href="/download/force_file_download_test.txt" download>source code</a>

More Blog: