# multipart/form-data: Boundary, Examples and Parsing

> The request body for forms that upload files. Each field travels as its own part, and a boundary string separates the parts.

- MIME type: `multipart/form-data`
- Used for: Request body
- Parameters: `boundary` — required, generated by the client
- Specification: [RFC 7578](https://www.rfc-editor.org/rfc/rfc7578)

## Example

```http
POST /upload HTTP/1.1
Host: api.example.com
Content-Type: multipart/form-data; boundary=----devkult7MA4YWxk

------devkult7MA4YWxk
Content-Disposition: form-data; name="title"

Quarterly report
------devkult7MA4YWxk
Content-Disposition: form-data; name="file"; filename="report.pdf"
Content-Type: application/pdf

%PDF-1.7 …binary data…
------devkult7MA4YWxk--
```

## Send it

### HTML form

```
<form action="/upload" method="post" enctype="multipart/form-data">
  <input name="title">
  <input type="file" name="file">
  <button>Upload</button>
</form>
```

### fetch with FormData

```
const body = new FormData();
body.append("title", "Quarterly report");
body.append("file", fileInput.files[0]);

// Don't set Content-Type yourself: the browser adds it with the boundary.
await fetch("/upload", { method: "POST", body });
```

### curl

```
curl -F title="Quarterly report" -F file=@report.pdf https://api.example.com/upload
```

## Receive it

### Express (multer)

```
import multer from "multer";

const upload = multer({ dest: "uploads/", limits: { fileSize: 10 * 1024 * 1024 } });

app.post("/upload", upload.single("file"), (req, res) => {
  // req.body.title, req.file.originalname, req.file.size
});
```

### Web standard Request (Next.js route handlers, Deno, Bun, Workers)

```
export async function POST(req: Request) {
  const form = await req.formData();
  const file = form.get("file") as File;
  const bytes = await file.arrayBuffer();
}
```

### Python (Flask)

```
from werkzeug.utils import secure_filename

@app.post("/upload")
def upload():
    title = request.form["title"]
    f = request.files["file"]
    f.save(os.path.join(UPLOAD_DIR, secure_filename(f.filename)))
```

### Go

```
r.ParseMultipartForm(10 << 20) // up to 10 MB in memory, the rest on disk
file, header, err := r.FormFile("file")
```

## Good to know

- Never set `Content-Type: multipart/form-data` by hand when sending a `FormData` body with fetch or axios. Without the `boundary` parameter the server can't split the parts — the usual cause of an empty `req.body` or a "Multipart: Boundary not found" error.
- Express's `express.json()` and `express.urlencoded()` skip multipart bodies. You need a multipart parser such as multer, busboy or formidable.
- A part's `filename` and its own `Content-Type` are whatever the client claims. Check the file's signature on the server and generate your own filename before saving — see the [MIME type list](https://www.devkult.com/mime.md) for signatures.
- For a single large file, a raw `PUT` with the file's own type (or a presigned upload URL) avoids the multipart overhead.

## FAQ

### When should I use multipart/form-data instead of application/x-www-form-urlencoded?

Whenever the form includes a file. URL encoding has no efficient way to carry binary data, while multipart sends each file as raw bytes in its own part. For forms with only short text fields, [x-www-form-urlencoded](https://www.devkult.com/mime/x-www-form-urlencoded.md) is smaller and simpler.

### What is the boundary in multipart/form-data?

A random string that doesn't occur in the content, declared in the header as `boundary=…`. Each part starts with `--` plus the boundary, and the body ends with `--`, the boundary and `--` again. When you use `FormData` or `curl -F`, the client picks it for you.

### Why is req.body empty when I send FormData?

Either the server has no multipart parser (Express needs multer or similar), or the Content-Type was set by hand and lacks the boundary. Remove the manual header and let the browser set it.

## Related types

- [application/x-www-form-urlencoded](https://www.devkult.com/mime/x-www-form-urlencoded.md)

Open the interactive page: https://www.devkult.com/mime/multipart-form-data