Also available in: Español · Português · Français · العربية
multipart/form-data builder and parser
Write the request a browser would send for a form upload, or paste one and find out what a server actually receives.
What multipart/form-data is
multipart/form-data is the format a browser uses to send a form that contains a file. Instead of one flat string of key-value pairs, the body is cut into parts, each separated by a delimiter called the boundary, and each carrying its own headers. Every part must declare Content-Disposition: form-data with a name parameter saying which form field it came from; a part representing a file adds a filename parameter and usually a Content-Type.
The boundary is the one mandatory parameter on the request's own Content-Type header, because without it a receiver has no way to find where one part ends and the next begins. It has to be a string that does not appear inside any part, and the MIME specification caps it at 70 characters. That is why real browsers generate something long and random-looking rather than something readable.
Two details of the format catch people out because they look like formatting noise. The line endings are CRLF pairs rather than bare line feeds, everywhere, including the blank line that separates a part's headers from its content. And the final delimiter carries two extra hyphens on the end — that is what says the body finished rather than being cut off in transit.
How to use it
- Pick a mode. Build a request takes a text field and a file field and writes the bytes a browser would send for them. Read a request takes a body you already have and works out the parts a server gets from it.
- In build mode, type the file name you care about. The sample buttons cover a plain name, an accented one, a name that cannot be read back, and one containing a double quote. Watch the row underneath: it shows what each name looks like on the wire, and whether the transformation can be undone.
- In read mode, paste the body and the boundary. You can paste the whole Content-Type header and the boundary will be pulled out of it. The table lists every part a receiver gets; the findings below split into errors, which break something, warnings, which mean the request was not built by a browser, and notes, which are worth knowing and are not faults.
Three specifications, three answers about the file name
Ask how to put a non-ASCII file name in an upload and you get three incompatible answers, all of them normative, and the reason this page exists is that the one everybody would reach for first is explicitly forbidden.
RFC 7578 settles that one in a note: the RFC 5987 encoding, which would add a filename* parameter to the Content-Disposition header, must not be used here. That is the exact opposite of the rule for a download. In an HTTP response, RFC 6266 requires filename* for any name that US-ASCII cannot express, and this site has a separate page about getting that right. Same header field, same parameter, opposite instruction — and the only thing deciding which applies is whether the header is travelling in a response or inside a request body.
The same section then offers a second answer: file names may be percent-encoded, following how a file: URI might be encoded. And in the very next paragraph it concedes the third, which is what actually happens: some commonly deployed systems send file names with octets outside US-ASCII directly, usually as UTF-8. That concession is the one worth reading twice, because a specification acknowledging that the field does something else is a specification telling you what your code will receive.
What browsers do is the third. The HTML Standard defines the encoding algorithm they follow, and it sends the name as raw bytes in the form's character encoding with no parameter announcing that fact. So a receiver that guesses wrong produces mojibake, and there is nothing in the request to correct it with. The builder here does what a browser does, and the row beneath it shows the byte count running ahead of the character count, which is that decision made visible.
The escaping cannot be undone
The HTML Standard's algorithm escapes exactly three characters in a field name or a file name: a line feed becomes %0A, a carriage return becomes %0D, and a double quote becomes %22. Then it closes the list in as many words — the user agent must not perform any other escapes.
That last clause is the interesting one, because it means the percent sign itself is never touched. A file genuinely named a%0Ab.txt and a file whose name contains a real line feed both arrive as a%0Ab.txt, and nothing in the request distinguishes them. The same collision happens for %0D against a carriage return and for %22 against a double quote. Measured against a real implementation of the algorithm rather than reasoned about: all three pairs come out identical.
So the transformation is one-way by design, and the obvious repair — escape the percent sign as well — is the thing the standard forbids. Anything that stores an uploaded file under the name it was given is quietly assuming this never happens. It is rare, and it is not impossible: a percent sign followed by two hexadecimal digits is exactly the shape a name picks up when it has already been through URL encoding somewhere upstream.
The tool reports it rather than fixing it, because there is no fix to apply. When a name shares its bytes with another name, the row underneath says so, and the reader can decide whether that matters for what they are building.
What this will not tell you
This reads the text you paste and nothing else. A browser cannot inspect another site's requests for you, so the tool has no idea whether the body you are looking at is the one being sent, whether the Content-Type header beside it matches, or what a server did with it afterwards.
The builder writes a body, not a request. It shows the Content-Type header that must accompany it, because the boundary lives there and the body is meaningless without it, but the rest of the request — the method, the path, the length — is yours. Paste the body into whatever client you are testing with rather than expecting this to send anything.
There is one deliberate simplification. Real part content is bytes, and a browser will happily put a JPEG in a part; this works in text, so the file part carries a placeholder rather than a real file. Everything about the structure, the headers, the escaping and the delimiters is exactly what a browser produces, and that is what the page is about — but do not measure a real upload's size from what you see here.
Finally, the format has an older mechanism this does not implement: Content-Transfer-Encoding, which once let a part be quoted-printable so it could survive a seven-bit transport. RFC 7578 tells senders not to generate one over a transport that carries binary data, and HTTP does, so the tool flags it if you paste one rather than pretending it is normal.
Why is it free?
Building and reading a request body is text work, and it runs in your browser. There is no server in the loop, so there is nothing to bill for and no account to create.
Nothing is uploaded and nothing is stored. Reload the page and it has forgotten what you pasted.