WTF is multipart/form-data in HTTP

sathwikreddy GVsathwikreddy GV
3 min read

Introduction

I've always wondered how files are sent to the server in an HTTP request. Whenever I need to create a UI for file upload on the frontend and send it to the backend using an API call, I set the content-type to multipart/form-data in my HTTP headers. But what is multipart/form-data and how does it actually send the data to the server? Let's dive deep into it...

Handling form data

What is form data? Form data is a format for sending data from a web form. Let's say you have a form in your application that a user needs to fill out and submit, which essentially makes a POST request. When you make a POST request, you need to encode the data that forms the body of the request in some way. HTML forms provide three methods of encoding.

  • application/x-www-form-urlencoded (the default)

  • multipart/form-data

  • text/plain

text/plain is rarely used because it doesn't encode special characters and has issues with new lines (read more about it here). application/x-www-form-urlencoded is the default encoding when you use the <form/> tag in your HTML code unless you specify the enctype as multipart/form-data. It is recommended to use multipart/form-data when the form data includes complex data like files, binary data, or non-ASCII characters. Otherwise, application/x-www-form-urlencoded is fast and efficient for small and simple data but not suitable for file uploads.

Don't worry about having a <form/> tag in your code whenever you have to upload files in the frontend. You can always use let fd = new FormData() and append whatever data you wanna send.

Structure of multipart/form-data

Let's examine the structure of multipart/form-data by setting up a simple loop that listens for incoming connections on port 8002 using netcat (nc).

The command will continuously listen for connections on port 8002. When a connection is made, it doesn't send any data (printf '' sends an empty string), and the loop restarts, ready to accept a new connection. Now, let's make a connection and send a file to the server at port 8002 using the command curl -F secret=@tick.gif http://localhost:8002, where tick.gif is a file in my folder. This command sends the file in an HTTP request. The -F flag tells curl to send the data as a multipart/form-data request. Let's look at the data printed by our while loop.

We can see that the host is localhost:8002 and the Content-Type is multipart/form-data. After that, we see a boundary and the binary data of the file starts. Here's how the data is sent when there are two (name, value) pairs. For example, the form sends two things to the server: a name and an image.

--boundary
Content-Disposition: form-data; name="name"

John Doe
--boundary
Content-Disposition: form-data; name="file"; filename="photo.jpg"
Content-Type: image/jpeg

(binary data)
--boundary--

The form data is divided into multiple parts and separated by a boundary. Each part includes headers that describe the content, followed by the data (text, binary, etc.). Now you know how multipart/form-data actually sends the data.

Conclusion

multipart/form-data encoding is a good choice when your form includes complex data like files. For simple and small data, it is not as efficient as application/x-www-form-urlencoded because of the overhead from boundary markers, which are needed for sending files and mixed data types.

0
Subscribe to my newsletter

Read articles from sathwikreddy GV directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

sathwikreddy GV
sathwikreddy GV

Seeking to make an impact in the field of software engineering.