Multipart/form-data field names are encoded incorrectly

I’m using Mailgun’s REST API in a Cloudflare Worker.

The API requires a POST of multipart/form-data. Some of the API’s form fields have a colon “:” character in their name:

My code looks something like this:

const body = new FormData();
body.set('v:preheader', 'lorem ipsum bla bla');
const response = await fetch(``, { body });

In chrome the resulting request body looks something like this:

Content-Disposition: form-data; name="v:preheader"

In a worker it looks like this, note the %3A instead of a “:” character. This is incompatible with the Mailgun API.

Content-Disposition: form-data; name="v%3Apreheader"

The spec is a bit ambiguous in this area… “:” is an ascii character so it should not be encoded per section 5.1:

Workaround for anyone who needs it:

  // Workaround:
  const brokenRequest = new Request('', {
    method: 'POST',
    body: formData // <-- instance of FormData
  // Get the content-type header (for the boundary).
  const contentType = brokenRequest.headers.get('content-type')!;
  // Get the body text.
  const bodyText = await brokenRequest.text();
  // Repair the body text.
  const body = bodyText.replace(
    /Content-Disposition: form-data; name="v%3A/g,
    'Content-Disposition: form-data; name="v:'
  // Create a new request using the extracted content type and repaired body text.
  const request = new Request(url.href, {
    method: 'POST',
    headers: {
      Authorization: `Basic ${btoa(`api:${apiKey}`)}`,
      'Content-Type': contentType
  const response = await fetch(request);

Hi @jdanyow, I think this is a bug. Our intent with percent-encoding form-data part names is to create a name that is safe for the recipient’s parser. However, that might just require us to encode quotation marks, and we should probably match Chrome’s behavior. I’ve filed a ticket to fix it, though I’m not sure when we’ll be able to do so.

1 Like

@harris sounds good, thanks!