browser-download-filename
Name downloaded files the way Chrome does. A pure-Python port of the Content-Disposition parser and the download naming rules of Chromium, the open-source core of Chrome, Edge and other browsers.
HTTP clients hand you a response, not a file name. The usual ways to get one
(the standard library's email module, RFC 6266 parsers on PyPI) disagree
with browsers on headers that real servers send: an ASCII fallback before
filename*, raw UTF-8, percent-encoded names, language tags, semicolons
inside quotes. On those headers, this package gives the name Chrome gives.
Features
- Chromium's answer — the header parser, the MIME type rules, the charset decoders and the file name sanitization are ports of Chromium's code at a pinned revision, and Chromium's own unit test cases are part of the tests. CI downloads more than 900 cases with a real Chromium and checks every result (How it is checked).
- The whole response — pass all the headers of a response. As in Chrome,
the first Content-Disposition value names the file, and the Content-Type
lines and X-Content-Type-Options give the MIME type, which can add an
extension to a name that does not come from Content-Disposition. Where
Chrome sniffs the body for another type, the package, which never sees the
body, keeps the declared one.
blocks_downloadtells you when Chrome refuses the response instead, for Content-Disposition values that differ or a NUL in the headers. - Bytes or str — pass the headers as the bytes on the wire, folded lines
included, or as the strs your HTTP library made of them, such as the Latin-1
strs of requests, urllib3 and
http.clientor the surrogate-escaped strs of aiohttp; name the codec asheaders_encoding, and they are mapped back to the bytes exactly. Some libraries join folded lines their own way first; the documentation lists them. - Never raises on a malformed header — it returns what Chromium makes of it.
- Safe names — the result is a single file name, never empty, with path
separators, control characters and other characters that file systems
reject replaced.
platform="windows"adds Windows' rules for reserved names, trailing dots and spaces, and shortcut extensions; by default they apply when the code runs on Windows.sanitize_filenamecleans a name from anywhere else, such as a page title, the same way. - No dependencies — pure Python, one wheel for every platform: CPython 3.11+ and PyPy.
Install
pip install browser-download-filename # or: uv add browser-download-filename
Usage
>>> from browser_download_filename import parse_content_disposition, suggested_filename
>>> header = b"attachment; filename=report.pdf; filename*=UTF-8''%E5%A0%B1%E5%91%8A.pdf"
>>> headers = [("Content-Disposition", header)]
>>> suggested_filename(headers, url="https://example.com/download?id=42")
'報告.pdf'
>>> suggested_filename(headers)
'報告.pdf'
>>> suggested_filename(url="https://example.com/files/r%C3%A9sum%C3%A9.pdf")
'résumé.pdf'
>>> parse_content_disposition('attachment; filename="a;b;c.txt";')
ContentDisposition(type='attachment', filename='a;b;c.txt', blocks_download=False)
suggested_filename downloads nothing: it takes the headers of a response and
its final URL (after redirects), and returns the name Chrome reports to
DevTools and Playwright as the download's suggested file name, except in the
cases listed under Limits of the
port.
The URL is optional: when the headers give no name, Chrome takes the last
segment of its path. Leave it out if you have no URL. With httpx2:
import httpx2
from browser_download_filename import suggested_filename
with httpx2.stream("GET", url, follow_redirects=True) as response:
name = suggested_filename(response.headers.raw, url=str(response.url))
response.headers.raw holds the header lines in bytes, each line on its own.
The documentation shows the same for httpx, requests, aiohttp and
urllib.request.
sanitize_filename cleans a name from anywhere else, such as a page title or
user input, as Chrome cleans an <a download> value:
>>> from browser_download_filename import sanitize_filename
>>> sanitize_filename("Q3 report: draft?.pdf")
'Q3 report_ draft_.pdf'
Documentation
Full documentation (English / 正體中文), including usage with httpx2, httpx,
requests, aiohttp and urllib.request, the options, how Chrome, Firefox and
Safari differ, how the package is checked against Chromium, the API reference
and a changelog:
https://birdhackor.github.io/browser-download-filename/
License
Licensed under either of MIT or Apache-2.0 at your option.
The package contains code ported from Chromium (BSD-3-Clause) and tables
generated from ICU and the WHATWG Encoding Standard (Unicode-3.0 and
BSD-3-Clause), so the distributions as a whole are under
(MIT OR Apache-2.0) AND BSD-3-Clause AND Unicode-3.0; see
THIRD-PARTY-LICENSES.
Metadata
Release files for browser-download-filename 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| browser_download_filename-0.2.0.tar.gz | 112.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| browser_download_filename-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 165.0 kB
Release files / browser_download_filename-0.2.0.tar.gz
| Download URL | browser_download_filename-0.2.0.tar.gz |
|---|---|
| Size | 112.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0d0eede0296fca357dfa6addd2c04c5223f13bce46324389646810d5caf9c022
|
|
BLAKE2b-256 checksum How to use checksums |
33e767aeeb2ddb2aaaf518164200e6c0b41f1a190987809e0d9818cff8ded049
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.
Transparency logRelease files / browser_download_filename-0.2.0-py3-none-any.whl
| Download URL | browser_download_filename-0.2.0-py3-none-any.whl |
|---|---|
| Size | 52.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8da7e63d4dc7cff19c34884cdc83894e939b9c7efbbd571750b604fd4e35507e
|
|
BLAKE2b-256 checksum How to use checksums |
0c6d12f1dee6a3cda0f7734f73bd40a7c16258501ea010e9e9828bb62e7ade02
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.
Transparency log