Cover image for An ode to Cross-origin image downloads

An ode to Cross-origin image downloads

pencillr profile image Richard Lenkovits ・3 min read


So I'm developing this app, and my client is like: "Can I have just a download button for the images pls?" And all hell broke loose.

Longer preface

Not long ago I started working on a resource sharing application for a client of mine. They're a design studio, and they wanted to have an app where they can distribute their work/images/source files, etc to their clients. The idea was to have a system where you can create and manage artifacts, users, user groups, and you're able to set up arbitrary access rights between them.

Basically what they needed was something that my designer colleague described as a "google drive on drugs".

So we made it. How? that's a different story.
Anyway. One day my client was like, "It's cool that we can look at the images and right-click download them, but can we have a download button as well?". I was like "sure should be fairly simple". It was not.

Intricacies of the anchor tag

The first solution I found was simply using an anchor tag.
So the anchor tag has this download attribute. Our illustrious helper w3Schools describes it with a fairly simple example:

<a href="/images/myw3schoolsimage.jpg" download="w3logo">

As simple as it could be - so I thought, and gave it a go. But my browser did not give a damn about it and just displayed the image in the same tab. What I did not calculate with was that my images and all artifacts were served from an AWS S3 bucket.

Same origin policy problems

Turns out that in most browsers to be compliant with the same-origin policy, the download attribute only works for same-origin URLs. Hence, it cannot be used to download resources served from a different origin. This is well described in this chrome feature update doc.

Anyway even if it's served from the same origin there can be problems, which need to be resolved using Content-Disposition header. I'm not going to go into this but here's a great article about the topic.

Shortly: To inform the client that the content of the resource is not meant to be displayed, the server must include an additional header in the response. The Content-Disposition header is the right header for specifying this kind of information.

Looking for Workarounds

The first thing I started to look for is some kind of workaround. Cross-origin image usage is well described at the corresponding Mozilla page, but I needed a download. The following StackOverflow post looked like the best approach for me. The point is that :blob and :data are unaffected so we can use a workaround using these, then fetch and emulating the download click.

CORS Errors

After this, a new problem came up thanks to the Cross-Origin Resource Sharing policy. You can read up on the whole thing here. Read it. Seriously it's great and funny. I'm just putting here one quote:

"JavaScript also did not have any special methods to perform network requests at that time. It was a toy language to decorate a web page."

All I needed was to set up CORS on the AWS side like this but you might also be able to use a trick with cors proxies like cors-anywhere.

I hope these links helped others who end up having similar issues.


Cover image base Designed by katemangostar / Freepik"

Posted on by:

pencillr profile

Richard Lenkovits


Web developer, devOps guy, quasi-physicist, ex-bartender.🍸


markdown guide

Thank you! Very informative. I didn't know about the download attribute and its implications.