content-disposition

Create and parse HTTP Content-Disposition headers, handling RFC 6266 filenames and RFC 8187 Unicode encoding.

Library
npm
v3.0.0
247 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
62 /100 Good
Development Activity 60
Maintenance 36
Community 72
Maturity 60
Momentum 20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
79 /100 Good
Architecture 80
Code Quality 88
Innovation 62
Learning Curve 85

content-disposition is a focused, zero-dependency TypeScript library from the jshttp organization for building and parsing the HTTP Content-Disposition header used in file downloads and multipart form uploads. It exposes three functions — create, parse, and format — that cover both directions of the header: generating a spec-correct value for a response (including safe fallback filenames for non-ASCII names) and decoding a header string a server or client receives, including the RFC 8187 extended (filename*) parameter form used for Unicode filenames.

The library is a byte-level, hand-written parser rather than a regex-based wrapper: it walks the header character by character to tokenize type, parameters, and quoted-string values per RFC 2616 grammar, and separately supports the multipart/form-data quoting conventions browsers use for uploaded file parts. This gives it correctness on edge cases (escaped quotes, backslash-escaped characters, malformed percent-encoding) that naive implementations get wrong, while keeping the runtime dependency-free and the bundle small.

It is maintained by the jshttp project — the same group behind type-is, accepts, and other headers used throughout the Express/Connect middleware ecosystem — and is a transitive dependency of Express itself, making it one of the most widely executed pieces of header-parsing code in the Node.js ecosystem even though most developers never import it directly.

What You Get

  • A create(filename, options) function that builds a spec-correct attachment header, automatically generating a US-ASCII fallback filename when the given filename contains non-ASCII characters
  • A parse(header, options) function that tokenizes a Content-Disposition header string into { type, parameters }, decoding RFC 8187 extended parameters (filename*) into their normal counterpart automatically
  • A format(obj, options) function for serializing an already-built { type, parameters } object back into a header string, with automatic extended-parameter encoding for non-ISO-8859-1 values
  • Built-in multipart mode on both parse and format that follows the WHATWG multipart/form-data quoting rules browsers use, rather than raw HTTP header grammar
  • Exported encodeExtended/decodeExtended helpers implementing RFC 8187 encoding directly, for callers who need just the Unicode-filename transform
  • Full TypeScript type definitions shipped in the package with no separate @types install needed

Common Use Cases

  • Setting the Content-Disposition header on a file-download response in a Node.js HTTP server so browsers save the response with the correct filename
  • Parsing the Content-Disposition header on an incoming multipart file upload to recover the original filename and form field name
  • Round-tripping Unicode filenames (e.g. non-Latin scripts or emoji) between browser and server without corruption, using RFC 8187 extended encoding
  • Building alternative-download or export endpoints (CSV/PDF/report generation) that need to suggest a save-as filename
  • Auditing or rewriting proxy/middleware layers that need to inspect or normalize Content-Disposition headers passing through them

Under The Hood

Architecture

The entire library lives in a single TypeScript module (src/index.ts) that exposes just three public functions — create, parse, format — plus small encodeExtended/decodeExtended helpers. Internally, parse is a hand-written character-code state machine (using skipOWS, trailingOWS, and parseToken helpers) that walks the header string once, branching on semicolons, equals signs, and quote characters to tokenize type and parameters directly against RFC 2616 grammar, rather than delegating to a general regex-based tokenizer. Parameter storage uses a NullObject (a prototype-less object built via a bare constructor function) as a micro-optimization over Object.create(null). There is no internal layering to speak of because the problem is intentionally narrow, and that scope match is itself the architectural decision.

Tech Stack

Written in TypeScript targeting Node.js 22+, built and linted via the shared @borderless/ts-scripts toolchain, and tested with Vitest (including a dedicated index.bench.ts for microbenchmarks). The package ships as ESM only ("type": "module"), compiles to a dist/ directory via a prepare build step, and declares zero runtime dependencies — everything needed to parse and format the header is implemented in-house rather than pulled in from elsewhere.

Code Quality

Test coverage is unusually thorough for the package’s size: three spec files (create.spec.ts, format.spec.ts, parse.spec.ts) total roughly 1,900 lines against about 520 lines of implementation, covering edge cases like malformed percent-encoding, escaped quotes, and RFC 8187 charset/language parsing. CI runs the suite across two Node versions via GitHub Actions with Codecov coverage reporting, and the repo also runs CodeQL static analysis and an OpenSSF Scorecard workflow. Types are used throughout with no any escape hatches in the public API surface.

API Design The public API is deliberately minimal — three functions with clear input/output shapes, matching how the header is actually used (build one, parse one, or reformat an already-parsed object). Ergonomics lean toward correctness over convenience: callers get automatic ASCII-fallback generation and RFC 8187 encoding for Unicode filenames without needing to understand the underlying extended-parameter grammar themselves, and a multipart option switches parsing/formatting to match how browsers actually quote multipart/form-data parts rather than forcing callers to pre- or post-process the difference.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers