Skip to content

FormData multipart boundary format doesn't match browser conventions #432

@harinagasaimadhav

Description

@harinagasaimadhav

Description
When using impit.fetch() with a FormData body, the generated multipart boundary uses an internal format (----formdata-impit-00000000.493) instead of the browser-standard WebKitFormBoundary format used by Chrome and Safari. Some servers fingerprint or validate the boundary prefix and reject requests that don't match the expected browser format, causing form fields to be silently dropped.

Steps to reproduce

import {Impit} from "impit";

const impit = new Impit({ browser: "chrome142" });
const form = new FormData();
form.append("field", "value");

const res = await impit.fetch("https://httpbin.org/post", {
  method: "POST",
  // headers: {"Content-Type":"multipart/form-data; boundary=----WebKitFormBoundaryKwwEDy3Dhp2M2R8S",}, // adding header manually  causing form fields to be dropped
  body: form,
});

const body = await res.json();
console.log(body);

Expected behavior
When browser: "chrome142" is set, the multipart boundary should match Chrome's format:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary[A-Za-z0-9]{16}

Actual behavior
Content-Type: multipart/form-data; boundary=----formdata-impit-00000000.493
This boundary leaks the impit implementation detail and breaks fingerprint-sensitive servers.

Workaround
Manually build the raw body and set the Content-Type header explicitly:

import {Impit} from "impit";

function generateBoundary() {
  const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  return "----WebKitFormBoundary" +
    Array.from({ length: 16 }, () => chars[Math.floor(Math.random() * chars.length)]).join("");
}

const boundary = generateBoundary();
const body = `--${boundary}\r\nContent-Disposition: form-data; name="field"\r\n\r\nvalue\r\n--${boundary}--\r\n`;


const impit = new Impit({
  browser: "chrome142",
  ignoreTlsErrors: true
});
const response =await impit.fetch("https://httpbin.org/post", {
  method: "POST",
  headers: { "Content-Type": `multipart/form-data; boundary=${boundary}` },
  body,
});

const responseBody = await response.text();

console.log(response);
console.log(responseBody);

Environment

impit version: 0.13.0
browser profile: chrome142
runtime: 
    deno 2.7.12 (stable, release, x86_64-pc-windows-msvc)
    v8 14.7.173.7-rusty
    typescript 5.9.2

Suggested fix
When a browser profile is set, the boundary generator should match that browser's known format. For Chrome/Safari profiles, use ----WebKitFormBoundary + 16 alphanumeric chars. For Firefox, use ----geckoformboundary + hex string.

Metadata

Metadata

Assignees

No one assigned

    Labels

    t-toolingIssues with this label are in the ownership of the tooling team.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions