What to do when the HTML download attribute is ignored

It turns out web browsers will usually ignore the <a download="filename"> HTML attribute on cross-origin requests.

The answer is for the server to set the HTTP Content-Disposition header in the response:

Content-Disposition: attachment;

This assumes the filename on the server is correct. In my case (for complicated and boring reasons), it is not, so I also need to set a filename in in the Content-Disposition HTTP header, e.g. Content-Disposition: attachment; filename="example.pdf".

In my case, the files are stored on a Google Storage bucket. Their name is not the same as the name the user wants (e.g. the file is called export_yVW4Bg-f63rpZIUiXvWct.pdf but the user wants export_31Jan2022.pdf). So when I create the file, I also need to set the Content-Disposition header accordingly.

This code snippet is part of a Google Cloud Function running on NodeJS, and I’m using the @google-cloud/storage library:

const { Storage } = require("@google-cloud/storage");

// Set the metadata to make the PDF download correctly
const setFileMetadata = async (
  bucketName,
  fileName,
  downloadFileName
) => await storage
      .bucket(bucketName)
      .file(fileName)
      .setMetadata({
        contentDisposition: `attachment; filename="${downloadFileName}"`,
        contentType: contentType,
      });

await setFileMetadata("my-unique-bucket-name", "export_yVW4Bg-f63rpZIUiXvWct.pdf", "export_31Jan2022.pdf");

Google’s engineers have posted a more comprehensive code sample, showing how you can also set other headers (e.g. cache-control) and metadata on the file.