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