The
html form enctype
attribute tells the browser how to encode form data before sending it to the server. Most of the time you never need to think about it, but the moment you add a file upload field to your form, picking the wrong enctype silently breaks everything. Here is exactly what each value does and when to use it.
Content Table
What enctype actually controls
When a form submits, the browser packages up all the field values into an HTTP request body. The
enctype
attribute (short for "encoding type") sets the
Content-Type
header on that request, which tells the receiving server how the data is formatted so it can parse it correctly.
Enctype only applies to
method="POST"
forms. If your form uses
GET
, field values go into the URL query string and enctype is ignored entirely.
The three enctype values explained
1. application/x-www-form-urlencoded (the default)
If you leave
enctype
off your form tag entirely, this is what you get. The browser serializes all field names and values into a URL-encoded string where spaces become
+
and special characters become percent-encoded sequences (for example,
&
becomes
%26
).
name=Jane+Doe&email=jane%40example.com&message=Hello+there
This format is compact and universally supported. It works perfectly for text-only forms: contact forms, login forms, search boxes, newsletter signups. The one thing it cannot handle is binary data like files, because encoding a multi-megabyte image as percent-encoded text would be enormous and unreliable.
2. multipart/form-data
This is the form data content type you must use whenever your form includes an
<input type="file"/>
field. Instead of a single encoded string, the browser splits the request body into separate "parts" divided by a randomly generated boundary string. Each part carries its own mini-headers, including a
Content-Disposition
describing the field name, and for files, a
Content-Type
describing the file's MIME type.
<form action="/upload" enctype="multipart/form-data" method="POST">
<input name="username" type="text"/>
<input name="avatar" type="file"/>
<button type="submit">Upload</button>
</form>
The raw request body looks roughly like this:
--boundary123
Content-Disposition: form-data; name="username"
janedoe
--boundary123
Content-Disposition: form-data; name="avatar"; filename="photo.jpg"
Content-Type: image/jpeg
[binary file data here]
--boundary123--
The multipart format preserves binary data exactly, which is why it is the required choice for form enctype file upload scenarios. The trade-off is that it is slightly more verbose for text-only data, so there is no reason to use it when you have no file inputs.
3. text/plain
This value encodes the form as plain text with each field on its own line in
name=value
format, without any URL encoding. The
WHATWG HTML Living Standard
includes it mainly for debugging purposes. In practice, avoid it for real form submissions because it is not reliably parseable by servers and offers no security benefits. You will rarely see it in production code.
Enctype and file uploads
This is where getting enctype wrong causes real, silent failures. If you have a file input but forget to set
enctype="multipart/form-data"
, the browser still submits the form but sends only the filename as a plain text string, not the actual file contents. Your server receives something like
avatar=photo.jpg
with no binary data attached. No error, no warning, just a missing file.
<input type="file"/>
needs both
method="POST"
and
enctype="multipart/form-data"
. Missing either one means the file never arrives.
When you submit a multipart form via JavaScript using the
FormData API, do not set the
Content-Type
header manually. The browser needs to generate the boundary string itself and include it in the header. If you override the header with a hardcoded
multipart/form-data
string, the boundary will be missing and the server will not be able to parse the parts.
// Correct: let the browser set Content-Type automatically
const formData = new FormData(document.querySelector('form'));
fetch('/upload', {
method: 'POST',
body: formData
// Do NOT add headers: { 'Content-Type': 'multipart/form-data' }
});
When you can safely omit enctype
For the vast majority of forms, you do not need to specify enctype at all. Contact forms, login screens, feedback forms, and search forms all contain only text fields. The default
application/x-www-form-urlencoded
encoding handles them efficiently and every server-side framework knows how to parse it.
If you are building a simple contact form for a static site, for example, you only need the
action
and
method
attributes. Speaking of which, understanding
what the form action attribute does
is just as important as getting enctype right, since both attributes together determine where your data goes and how it arrives.
Similarly, if your form includes a
<textarea>
</textarea>
for multi-line messages, the default encoding handles newlines and special characters just fine. You can learn more about working with textarea elements in
this guide to the HTML textarea element.
Common enctype mistakes and how to fix them
-
File input without multipart/form-data:
The file is not uploaded, only its name. Fix: add
enctype="multipart/form-data"to the form tag. - Using multipart/form-data on a text-only form: Not harmful, but adds unnecessary overhead. Remove enctype and let the browser use the default.
- Setting Content-Type manually in fetch when using FormData: Breaks the multipart boundary. Remove the manual header and let the browser handle it.
- Using enctype on a GET form: Enctype is ignored for GET requests. Data always goes in the URL query string regardless of what you set.
-
Typos like
multipart-form-data(hyphen instead of slash): The browser falls back to the default encoding silently. The correct value uses a forward slash:multipart/form-data.
Quick reference table
| Enctype value | Use case | Handles files? | Default? |
|---|---|---|---|
application/x-www-form-urlencoded
|
Text-only forms (contact, login, search) | No | Yes |
multipart/form-data
|
Any form with a file input | Yes | No |
text/plain
|
Debugging only | No | No |
One more thing worth knowing: the
enctype
attribute can also be set on a submit button using the
formenctype
attribute. This overrides the form-level enctype for that specific button only, which is useful if you have one form that needs to submit to two different endpoints in different formats. The
MDN documentation on formenctype
covers the full details.
For contact forms on static sites where you are just collecting a name, email, and message, you can skip enctype entirely and focus on securing your submissions properly. A good starting point is understanding how to secure form submissions against CSRF and spam.
Add a working contact form to any static site, no backend needed
Once you have your html form enctype sorted, SendForm handles the rest. Point your form's action at a SendForm endpoint and submissions land in your inbox, Slack, or CRM without writing any server code.
Try SendForm Free →
The browser submits the form using the default
application/x-www-form-urlencoded
encoding. Instead of sending the file's binary contents, it sends only the filename as a plain text string. Your server receives something like
avatar=photo.jpg
with no actual file data. There is no browser error or warning, so this failure is easy to miss during development.
When you use the FormData API with fetch, the browser automatically sets the correct
multipart/form-data
content type and generates the boundary string. The HTML enctype attribute on the form tag is not used in this flow. The important rule is to never manually set the Content-Type header in your fetch call when sending a FormData object, because that removes the boundary and breaks parsing on the server side.
Yes, it is technically valid. The server will receive the text fields correctly. However, the multipart format adds more bytes to the request than the default URL-encoded format does for plain text data, so there is no benefit and a small performance cost. Stick with the default encoding for text-only forms and reserve
multipart/form-data
for forms that actually include file inputs.
The
enctype
attribute on the HTML form tag tells the browser which Content-Type header to set when it submits the form. They describe the same thing from two different angles: enctype is the HTML-side instruction, and Content-Type is the resulting HTTP header the server actually reads. When you submit programmatically with fetch, you control the Content-Type header directly instead of relying on the enctype attribute.
No. For GET forms, the browser always appends field values to the URL as a query string, regardless of what enctype is set to. Enctype is only meaningful for POST requests, where the data goes in the request body. Setting enctype on a GET form is harmless but has no effect on how the data is sent.
The
formenctype
attribute on a submit button overrides the form's enctype for that button only. If a form has two submit buttons, one with
formenctype="multipart/form-data"
and one without, clicking each button will submit the same form with a different encoding. This is useful when one form needs to handle both file and non-file submission paths, though in practice it is an uncommon pattern.