DEV Community

Cover image for Forcing a browser to download a file (HTML and server side)
Nick | OneThingWell.dev
Nick | OneThingWell.dev

Posted on • Edited on • Originally published at betterways.dev

Forcing a browser to download a file (HTML and server side)

When a user clicks a normal HTML link to a file:

<a href="path/to/file.ext">
Enter fullscreen mode Exit fullscreen mode

Browsers will usually try to open the file directly (inline) instead of opening the file download dialog.

You can use HTML5 download attribute to tell modern browsers to open the download dialog instead:

<a href="path/to/file.ext" download>
Enter fullscreen mode Exit fullscreen mode

You can also specify the suggested filename the user will see in the download dialog:

<a href="path/to/file.ext" download="myfile.ext">
Enter fullscreen mode Exit fullscreen mode

If you specify just the file name (without the extension), the extension will usually be inferred from the original filename (this example will still suggest "myfile.ext" in most browsers):

<a href="path/to/file.ext" download="myfile">
Enter fullscreen mode Exit fullscreen mode

Limitations and workarounds

Nowadays, download attribute is supported in all mainstream browsers, but there's an important limitation: this attribute only works for same-origin URLs (protocol, port, and host have to match), and the blob: and data: schemes.

Besides the download attribute, there's not much else you can do to force this behavior from the client side, but you can on the server side.

Including the Content-Disposition header in the HTTP response will have a similar effect to download attribute (but without this limitation):

Content-Disposition: attachment; filename=file.ext
Enter fullscreen mode Exit fullscreen mode

You'll also need to send the Content-type header and the actual file contents.

In PHP, that would look something like this:

header('Content-Disposition: attachment; filename="downloaded.pdf"');
header('Content-type: application/pdf');
readfile('test.pdf');
Enter fullscreen mode Exit fullscreen mode

But to avoid the performance overhead, I recommend doing that on the webserver level instead of the app level.

nginx

For nginx this could be as simple as:

add_header Content-Disposition 'attachment; filename="file.ext"';
Enter fullscreen mode Exit fullscreen mode

Note: nginx will usually send the correct Content-type header automatically, but you can override that if needed:

add_header Content-Type 'application/pdf'
Enter fullscreen mode Exit fullscreen mode

For a more general case, you could send a generic application/octet-stream header and Content-Disposition without a filename:

location /myloc {
        if ($request_filename ~* ^.*?\.(pdf|zip|docx)$) {
            add_header Content-Disposition attachment;
            add_header Content-Type application/octet-stream;
        }
    }
Enter fullscreen mode Exit fullscreen mode

Combination of download attribute and Content-Disposition header

If both the download attribute and Content-Disposition header are present and both define the filename, the filename defined in the header will have priority.

In the case of Content-Disposition: inline header (which tells the browser to display the contents inline - as a part of the page), the download attribute will have the priority (for the same-origin URLs).




Note: This is a snapshot of the page from the BetterWays.dev wiki, you can find the latest (better formatted) version here: betterways.dev/forcing-a-browser-to-download-a-file.

Top comments (0)