Chrome 65 blocks cross-origin <a download>. Client-side workaround to force download?

Solution 1:

According to the discussion blob: and data: URLs are unaffected, so here is a workaround using fetch and Blobs.

Client-side force download media

function forceDownload(blob, filename) {
  var a = document.createElement('a');
  a.download = filename;
  a.href = blob;
  // For Firefox https://stackoverflow.com/a/32226068
  document.body.appendChild(a);
  a.click();
  a.remove();
}

// Current blob size limit is around 500MB for browsers
function downloadResource(url, filename) {
  if (!filename) filename = url.split('\\').pop().split('/').pop();
  fetch(url, {
      headers: new Headers({
        'Origin': location.origin
      }),
      mode: 'cors'
    })
    .then(response => response.blob())
    .then(blob => {
      let blobUrl = window.URL.createObjectURL(blob);
      forceDownload(blobUrl, filename);
    })
    .catch(e => console.error(e));
}

downloadResource('https://giant.gfycat.com/RemoteBlandBlackrussianterrier.webm');

However, fetch only works on some URLs. You may get a CORS error:

Failed to load https://i.redd.it/l53mxu6n14o01.jpg: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://redditp.com' is therefore not allowed access.

There are extensions that let you intercept and modify or delete websites' security headers:

ModHeader - Chrome Web Store

(But setting Access-Control-Allow-Origin: * broke YouTube for me)

Performance

Please note that this approach isn't very performant! At times I've had downloads stall for <1min. The rest of the page was responsive during this time though. I haven't looked into this, but I imagine creating large Blobs is resource intensive.

Violentmonkey / Tampermonkey

If your use case is userscripts, there's GM_download(options), GM_download(url, name)

⚠ In Tampermonkey this is a beta feature, and you must first set Download Mode: [Browser API ▾] in Tampermonkey Dashboard > Settings

Tampermonkey Dashboard > Settings > Downloads

Solution 2:

Apparently, the web specification changed at some point to disallow cross-origin downloads. Add content-disposition: attachment header in the response and cross-origin downloads may work again.