A download filename is presentation metadata; derive it from authorized server data and let ContentDisposition encode the header.
Spring MVC Content-Disposition: produce a safe attachment filename
Separate identity from a label
A client may request receipt 47 and submit an arbitrary filename in the URL. The storage lookup must use the authorized receipt identity, while the attachment label comes from a controlled server field. Restrict the label to an expected form such as receipt-47.pdf and keep its extension consistent with the response media type. Byte-range delivery] uses the same rule on every partial request.
Let the header builder encode
Content-Disposition has quoting and non-ASCII filename rules. Assemble it with ContentDisposition.attachment().filename(name, UTF_8) rather than concatenating raw user text into a header. Encoding protects header syntax, but it does not make an untrusted path component safe for storage or prevent misleading names. Normalize or reject separators, controls and unexpected extensions before building the value.
Check the client-facing result
Test ASCII and non-ASCII controlled labels and inspect the parsed Content-Disposition value. Also test a malicious submitted name; the server label should remain unchanged. A browser may choose its own saved filename despite the header, so tests should assert the response contract rather than a particular browser dialog.
Implementation contract
String attachmentName = "receipt-47.pdf"; // Derived from server metadata.
ContentDisposition attachment = ContentDisposition.attachment()
.filename(attachmentName, StandardCharsets.UTF_8)
.build();
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, attachment.toString())
.contentType(MediaType.APPLICATION_PDF)
.body(receiptStorage.authorizedFile(authentication, receiptId));Cost and verification
Header construction is constant work. The expensive operation is reading the attachment; a misleading or unsafe name creates client-side confusion even when bytes are correct.
Common Mistakes
- Do not paste a client-supplied filename into the header.
- Do not confuse header encoding with filesystem path validation.
- Do not advertise .pdf while returning another media type.
Read next
Spring MVC byte ranges: resume an authorized receipt download, Spring MVC consumes and produces: 415 and 406 are different failures, Spring MVC multipart receipt import: validate before storing, Spring multipart size limits: enforce parser and application budgets, Spring MVC private Cache-Control: decide who may retain a receipt.
