Repository navigation
Content-Disposition without quoted parameters fails #4565
Description
When the parameters in the Content-Disposition HTTP header field are not surrounded by double quotes, the request is rejected by the server with error 500. According to https://datatracker.ietf.org/doc/html/rfc5987#section-3.2.2, unquoted values should be permitted. The example request below originates from HttpClient, which is part of the .NET libraries. HttpClient uses unquoted values, unless quoting is required due to the presence of special characters.
While it is possible to use custom code to manually patch up the request (see below), this prevents us from using auto-generated client libraries based on an OpenAPI spec file.
POST https://api.example.com/v3/packages/e2c923a3-26d8-449a-acef-201052915e7f/upload HTTP/1.1
Host: api.example.com
Authorization: Bearer XXX
Content-Type: multipart/form-data; boundary=a63df4c1-ad7c-4c18-b393-1cf5d2fccca4
Content-Length: 3791
--a63df4c1-ad7c-4c18-b393-1cf5d2fccca4
Content-Type: application/octet-stream
Content-Disposition: form-data; name=bits; filename=PushTestApp.zip; filename*=utf-8''PushTestApp.zip
Content-Length: 3540
***BINARY DATA***
--a63df4c1-ad7c-4c18-b393-1cf5d2fccca4--HTTP/1.1 500 Internal Server Error
Content-Length: 144
Content-Type: text/html
Date: Tue, 16 Sep 2025 07:44:26 GMT
X-Vcap-Request-Id: fa305c34-a213-47e9-67f8-52970547ccfd
<html>
<head><title>500 Internal Server Error</title></head>
<body>
<center><h1>500 Internal Server Error</h1></center>
</body>
</html>The following C# code can be used to manually add the quotes:
var content = new MultipartFormDataContent
{
{
new StreamContent(stream)
{
Headers =
{
ContentType = new("application/octet-stream")
}
},
//"bits", "PushTestApp.zip" /* original, does not work */
"\"bits\"", "\"PushTestApp.zip\"" /* manual patch-up, but breaks filename* value */
}
};The code above results in the following header value, where the filename* value has now become incorrect (because there are quotes inside the filename value).
Content-Disposition: form-data; name="bits"; filename="PushTestApp.zip"; filename*=utf-8''%22PushTestApp.zip%22To compensate, additional custom code is required after construction:
content.First().Headers.ContentDisposition!.FileNameStar = "PushTestApp.zip"; // without quoteswhich results in:
Content-Disposition: form-data; name="bits"; filename="PushTestApp.zip"; filename*=utf-8''PushTestApp.zipAlternatively, the filename* parameter can be removed after construction:
content.First().Headers.ContentDisposition!.FileNameStar = null;which results in:
Content-Disposition: form-data; name="bits"; filename="PushTestApp.zip"This is all very inconvenient. We're fighting against the framework defaults. It's not reasonable to expect auto-generated client libraries perform similar hacks. So please adapt the server to allow unquoted values.
Activity
philippthun commentedon Apr 30, 2026
This issue has been resolved by the Rack upgrade from 2.2.x to 3.2.4 in #4707 (merged 2025-12-19), which was unblocked by the Thin removal in #4670.
Rack 3.1 completely rewrote the Content-Disposition parameter parser with explicit handling for unquoted values. At
lib/rack/multipart/parser.rb#L436-L447:This handles the exact case from this issue -
name=bits; filename=file.zip(unquoted) is now parsed correctly alongside the standard quoted form.Upstream fix: rack/rack#2077 / rack/rack#2076 (first released in Rack 3.1.0, 2024-06-11)
Available since: CAPI release 1.224.0 (2026-01-08)