Uploading files programmatically using cURL keeps deployments repeatable, automates API interactions, and removes the need for manual dashboard uploads. File transfers for logs, images, backups, and build artifacts can be scripted, scheduled, and integrated into existing tooling without changing the server side. A single command can push the same file to a test endpoint, a staging bucket, and a production upload URL.

Under the hood, cURL relies on libcurl to speak protocols such as HTTP(S) and FTP(S). For web-style form uploads, cURL sends multipart/form-data using the --form option, mirroring what a browser does with an HTML file input. APIs that expect a raw file stream can be served by feeding the file directly as the request body via --data-binary, while servers that expose FTP or SFTP targets receive uploads via the --upload-file option.

File transfers can fail quietly or partially when authentication, timeouts, redirects, or size limits are involved. Large uploads benefit from verbose logging and explicit protocol choices such as FTPS or SFTP instead of plain FTP. Storing credentials in scripts or shell histories also carries risk, so environment variables, dedicated configuration files, or secrets managers are safer than embedding usernames and passwords directly in command lines.

Steps to upload files using cURL:

  1. Open a terminal in the directory containing the files intended for upload.
    $ pwd
    /home/user/uploads
  2. Send a single file to an HTTP endpoint as multipart/form-data using the default field name.
    $ curl --form "file=@./example.txt" https://httpbin.org/post
    {
      "files": {
        "file": "example file content"
      },
      "form": {},
    ##### snipped #####
    }

    --form automatically sets Content-Type: multipart/form-data and assigns the field name (file in this example) as if an HTML form posted the file.

  3. Include additional form fields when the HTTP endpoint expects metadata alongside the uploaded file.
    $ curl \
      --form "file=@./report.pdf" \
      --form "description=Quarterly report" \
      --form "owner=ops-team" \
      https://httpbin.org/post
    {
      "files": {
        "file": "PDF binary data"
      },
      "form": {
        "description": "Quarterly report",
        "owner": "ops-team"
      }
    ##### snipped #####
    }

    Multiple --form flags add extra parts to the same multipart request, which is useful for titles, tags, and simple JSON or text metadata.

  4. Upload a file as a raw request body when an API expects the file stream directly instead of multipart/form-data.
    $ curl --data-binary @./backup.tar.gz https://example.com/api/upload/raw
    {"status":"ok","size":1048576}

    --data-binary preserves the file bytes exactly and avoids stripping newlines, which distinguishes it from --data for structured form fields.

  5. Push a file over an SSH-based endpoint using SFTP with explicit credentials.
    $ curl --upload-file ./example.txt sftp://sftp.example.com/incoming/example.txt --user "username:password"
    % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
    ##### snipped #####

    Using plain ftp:// sends credentials and file contents unencrypted; sftp:// or ftps:// prevents easy interception on untrusted networks.

  6. Override the field name or content type when the remote API requires specific form names or media types.
    $ curl \
      --form "avatar=@./avatar.png;type=image/png" \
      https://example.com/profile/avatar
    {"ok":true,"field":"avatar"}

    The segment before the equals sign (avatar here) becomes the form field key, and the type attribute sets an explicit Content-Type header for that part.

  7. Confirm a successful upload by running the same HTTP request with verbose logging and checking the HTTP status and response body.
    $ curl --form "file=@./example.txt" https://httpbin.org/post --verbose
    *   Trying 3.222.190.64:443...
    * Connected to httpbin.org (3.222.190.64) port 443
    > POST /post HTTP/1.1
    > Host: httpbin.org
    ##### snipped #####
    < HTTP/1.1 200 OK
    ##### snipped #####

    Reliable confirmation signals include a 2xx HTTP status, echoed metadata showing the expected form fields, and presence of the uploaded object in the target storage or application UI.

Discuss the article:

Comment anonymously. Login not required.