CalcSnippets Search
CLI 3 min read

`curl -F` Multipart Uploads Are Easy Once You Stop Treating Them Like JSON Requests

A practical curl multipart guide showing how to send files and form fields with -F, why uploads fail when developers keep thinking in JSON-only terms, and how to debug the request shape quickly.

Why this trips people up: developers get used to JSON APIs, then the first file upload endpoint arrives and they keep trying to send it like another -H 'Content-Type: application/json' -d ... request. Multipart requests are a different shape.

What -F is doing

With curl, -F sends form fields as multipart form data. That is the right fit for endpoints expecting uploaded files or form-like request bodies.

A simple file upload example:

curl -F "[email protected]" https://example.com/upload

That @ matters. It tells curl to send the file contents, not the literal string.

File plus normal fields

Multipart requests often include metadata too:

curl \
  -F "[email protected]" \
  -F "user_id=42" \
  -F "category=finance" \
  https://example.com/upload

This is not the same as embedding everything into one JSON blob and hoping the server interprets the file path for you.

The mistake people keep making

They manually set:

-H "Content-Type: multipart/form-data"

without letting curl build the boundary correctly. In many cases, that is the wrong instinct. Multipart requests need a boundary, and curl already knows how to construct it when you use -F.

So the better habit is: use -F, and do not force headers you do not actually need to handcraft.

Why uploads still fail

Usually one of these is true:

  1. the field name is wrong
  2. the file path is wrong
  3. the server expected multipart but got JSON
  4. the client manually set a broken content type

That last one is more common than people expect. A lot of multipart pain is self-inflicted by trying to be too clever with headers.

Why -v is your friend here

If the request feels wrong, turn on verbose mode:

curl -v -F "[email protected]" https://example.com/upload

You are not trying to inspect every byte forever. You are trying to answer basic questions fast:

  • did curl send the request you thought it would
  • did the server reject the upload immediately
  • is the endpoint responding like a normal multipart handler

Fast visibility beats guesswork.

How to handle auth and custom field requirements

Real upload endpoints rarely accept only a single file. They often require auth headers, API keys, or extra fields such as folder IDs, message text, or media type hints.

That usually looks more like this:

curl \
  -H "Authorization: Bearer $TOKEN" \
  -F "[email protected]" \
  -F "purpose=profile-image" \
  https://example.com/upload

This is where people often panic and go back to a JSON mindset. They assume the whole request must be rewritten by hand because auth is involved. It does not. Auth headers and multipart fields happily coexist. The file still belongs in -F.

How to debug the server side expectation

If the backend says “missing file” or “unsupported media type,” inspect the server contract carefully:

  1. what exact field name is expected
  2. does the endpoint want one file or multiple files
  3. does it require additional text fields
  4. is there an auth header the frontend already includes

Many upload bugs are not transport bugs. They are contract-mismatch bugs. The client sent upload, the server expected file, and everybody lost thirty minutes pretending the multipart format itself was mysterious.

A practical mental model

Treat multipart as “a bag of named fields, some of which may contain file content.” That model is simple enough to debug from both sides. Once you think about it that way, curl -F stops feeling like a weird trick and starts feeling like the obvious tool.

Final recommendation

Treat multipart uploads as their own request shape instead of as “JSON plus a file somehow.” With curl, -F is the clean default, and the fewer homemade content-type tricks you add, the more likely the request will be correct. Upload debugging gets much easier once you let the tool do the multipart work it was designed to do.

Sources

Keep reading

Related guides