Send request data

Submit forms and files

Choose URL-encoded form data or multipart form data and upload a local file deliberately.

HTML forms send data in one of two encodings.

URL-encoded data packs the fields into name=value pairs, the same format as a query string, and sends them as the request body. Multipart data splits the body into parts separated by a boundary, and each part has its own headers.

Multipart is the normal choice when a field contains a file. Files are binary, and they don’t survive URL encoding well.

curl has one option for each. --data sends URL-encoded data. --form sends multipart.

A simple form

For text-only fields, URL-encoded is what most login and search forms use:

curl --data 'title=Lab notes' --data 'author=mina' https://httpbin.org/post

The echo shows both fields under form, and the Content-Type header reads application/x-www-form-urlencoded. Repeating --data joins the pairs with &, exactly like a browser does.

A form with a file

Create a small file, then send one text field and one file:

echo 'first line of my notes' > notes.txt
curl --form 'title=Lab notes' --form 'attachment=@notes.txt' https://httpbin.org/post

The response separates the form value from the file content:

{
  "files": {
    "attachment": "first line of my notes\n"
  },
  "form": {
    "title": "Lab notes"
  }
}

That split is your proof. title arrived as an ordinary field. attachment arrived as a file part, with its content intact. curl built the multipart boundary and the per-part headers for you. Those are the fiddly parts of the format, and you never want to write them by hand.

The @ prefix reads a local file and uploads it as a file part. If you want the file’s content sent as a plain text field instead, use < in place of @.

When the server cares about the MIME type, say it explicitly:

curl --form 'attachment=@notes.txt;type=text/plain' https://httpbin.org/post

The mistake to avoid

Check the path before you run a --form command copied from somewhere else. @ means “read this file from my disk and send it over the network”. Pasted blindly, something like --form 'config=@~/.ssh/id_ed25519' uploads your private key to whoever runs that server.

A typo in the path is safer than that, because curl refuses to continue:

curl: (26) Failed to open/read local data from file/application

Exit code 26 is a read error. You get a clear failure, not a silent empty upload.

One more trap. Mixing --data and --form in the same command doesn’t work. A request body has one content type. Pick the encoding the server expects and stick to it.

Try this with a form on a site you own: send the same fields with --data first, then with --form, and compare the Content-Type header in -v output.

Lesson completed

Take this course offline

Get every free book, course edition, and software download.

Get the download library →