You Can’t Prompt a File Download With the Content Disposition Header Using Axios (XHR). Sorry.

Emily Drevets
Mar 14 · 4 min read
You are sad like this clown boy in basket.

If you’re reading this article, I’m really sorry. I can feel your desperation. You know, you BELIEVE, that using the content disposition header should work in order to trigger a file download after making a call to an api with axios. But it doesn’t. Not for you. Not for anyone. And it never will. Never. Ever. Never, ever, ever. So, I’m sorry. Give up now.

Here’s why

(paraphrased from a slack message of @glebec)

Making a request via XHR (which axios, fetch, etc. abstract over), tells the browser that the response should be handled by JavaScript code.

i.e. It doesn’t owe you a fucking thing.

Why I have the misfortune of knowing this

Here’s what I wanted to happen:

  • A user hits the submit button in my SPA (yes, an actual SPA)
  • The submit button triggers a request via axios
  • An xml string is generated on the backend, based on the application’s state, and then sent back to the client
  • The user sees a download dialogue with the correct file name and file type in the box and can download the response data as a file. Yay.

Here’s how I thought I could make it happen:

  • Easy! Just set the Content-Disposition header on the response to “attachment; filename=myCoolFile.xml”
  • Simple! Just send the data back.
  • User just magically sees the download dialogue and I get a raise.

Here’s what didn’t happen:


Why why why why why why why why why why why

So, my server’s response had the content-disposition header of attachment.

"Content-Disposition": attachment; filename="cool.xml"

When I made the GET request directly via the browser, it worked perfectly. I saw the download dialogue box, the right file name, everything. I had everything!

This did work!
It did this!

That’s because, as I learned, the browser received that Content Disposition header and knew to display the content as an attachment and prompt the download.

It’s like the browser hit a volleyball over a net and then the server spiked it right back onto its face and it was like, oh I should do something about this.

But when I made the GET request via axios, the browser saw the header and the content-disposition set to attachment, but did not trigger the download dialogue because that wasn’t its job.

Clicking that button did nothing. NOTHING. Well, it made the request but did not trigger the download dialogue.

That’s because, as I learned, the response to a request made via axios should be handled by the JavaScript code, not by the browser showing anything to the user.

It’s like axios and the browser were both playing volleyball, but instead of playing, the browser was sticking its head up its own ass as far as it could go. (OK, maybe being a little unfair to the browser here, but you get it).

The Sad Thing You Might Have to Do

It’s ugly. It’s grotesque. It’s JavaScript. If you’re trying to make that friendly little download dialogue appear, then check out this sorry gist that does a gross thing that is hacky and embarrassing and, in our case, also production worthy.


It’s like Jay from Chicago Tech Slack said:

I’m sorry there’s not something better, Emily. I know you’ve been struggling with this for at least two weeks now.

I unfortunately still believe that there might be a way to make some of this magic happen on the server side so I don’t have to do that thing above here. Maybe generate a file, print it out, and then slap the user on the face with it via a man in a bear suit as part of a singing telegram service. I don’t know.

The moral of the story

This specific way to trigger a file download doesn’t work. It’s not your fault. It’s not my fault. It’s [fill-in-the-name-of-some-historical-character-you-don’t-like]’s fault, and they’re dead.

Find another way to make this happen.


P.S. Gabriel Lebec also suggested looking into creating a data URI on the client side, which seems like it could be a solution for downloading smaller files.

P.P.S. People that helped me: Jay Sherby from Chicago Tech Slack, Gabe Lebec from FullStack, Jami Gibbs because she’s awesome.

 by the author.

Emily Drevets

Written by

Software engineer in Chicago. JavaScript. Cashews. Bike.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade