Use the target filename from the server

HTTP servers have the option to provide a header named Content-Disposition: in responses. That header may contain a suggested filename for the contents delivered, and curl can be told to use that hint to name its local file. The -J / --remote-header-name enables this. If you also use the -O option, it makes curl use the filename from the URL by default and only if there is actually a valid Content-Disposition header available, it switches to saving using that name.

If there are multiple Content-Disposition headers, curl picks the name from the first one it sees.

By default curl saves that file in your current directory, which can be changed with --output-dir.

-J has some challenges and risks associated with it that users need to be vary of:

  • It only uses the rightmost part of the suggested filename; the path part is stripped out.

  • Since the filename is provided by the server, curl does not overwrite any preexisting local file in your current directory if the server happens to provide such a filename (unless you use --clobber).

  • filename encoding and character sets issues. curl does not decode the name in any way, so you may end up with a URL-encoded filename where a browser would otherwise decode it to something more readable using a sensible character set.

Location: too

The above approach mentioned works pretty well, but has limitations. One of them being that if the site instead of providing a Content-Disposition header only redirects the client to a new URL to download from, curl does not pick up the new name but instead keeps using the one from the originally provided URL.

Since curl 8.19.0, -J also picks up the filename from Location: headers and uses that filename if no Content-Disposition header arrives.

If you run a command line like:

curl -L -O -J https://example.com/download?id=6347d

Assuming the site redirects curl to the actual download URL for the tarball you want to download like this:

HTTP/1 301 redirect

Location: https://example.org/release.tar.gz

This makes curl save the contents of that transfer in a local file called release.tar.gz.

If there is both a redirect and a Content-Disposition header, the latter takes precedence.

What filename?

Since the selected final name used for storing the data is selected based on contents of a header passed from the server, using this option in a scripting scenario introduces the challenge: what filename did curl actually use?

A user can easily extract this information with curl’s -w option. Like this:

curl -w '%{filename_effective}' -O -J -L \
  https://example.com/download?id=6347d

This command line outputs the used filename to stdout.

Tweak the command line further to instead direct that name to stderr or to a specific file etc. Whatever you think works.