← All articles
HTMLFile UploadForms

How to add file upload to an HTML form

FormsFort Team ·

File uploads are essential for job application forms, document submission, support tickets with attachments, and any workflow where users need to send files alongside their form data.

This guide covers everything needed to add file upload to an HTML form, from basic markup to secure handling.

Basic file upload form

Two requirements for file uploads:

  1. The form must have enctype="multipart/form-data"
  2. The form must contain an <input type="file"> element
<form action="https://api.formsfort.com/submit" method="POST" enctype="multipart/form-data">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<label for="name">Name</label>
<input type="text" id="name" name="name" required />
<label for="resume">Resume</label>
<input type="file" id="resume" name="resume" />
<button type="submit">Submit application</button>
</form>

Without enctype="multipart/form-data", the browser sends the form as application/x-www-form-urlencoded and the file data is not included. This is the most common mistake.

Migrating a Contact Form 7 file tag

Contact Form 7 describes fields with tags, while a static HTML form describes them with elements. A file tag such as [file resume filetypes:pdf|doc|docx limit:10mb] becomes an <input type="file">; the CF7 options are not a portable backend policy by themselves. Use the Contact Form 7 to HTML converter for a starting point, then review the generated field name and rules.

CF7 tag:
[file resume filetypes:pdf|doc|docx limit:10mb]
<form action="https://api.formsfort.com/submit" method="POST" enctype="multipart/form-data">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<label for="resume">Resume (PDF, DOC, or DOCX; up to 10 MB)</label>
<input
type="file"
id="resume"
name="resume"
accept=".pdf,.doc,.docx"
/>
<button type="submit">Upload resume</button>
</form>

The 10 MB value above is an example policy for this form, not a universal HTML or FormsFort limit. Configure the actual FormsFort size, MIME, extension, and file-count rules in the form settings. Direct multipart uploads use file policy checks and a limited signature check before being queued as email attachments. If the workflow requires ClamAV scanning and temporary download links, use the advanced object-storage upload path described in the file upload backend guide.

Keep the field name (resume) stable if an integration expects it, but do not assume that a CF7 mail template, conditional-field extension, redirect, or spam integration transfers with the HTML. Re-test each behavior from the deployed domain.

Restricting file types

Use the accept attribute to limit which files the user can select:

<input type="file" name="document" accept=".pdf,.doc,.docx" />
<input type="file" name="photo" accept="image/*" />
<input type="file" name="spreadsheet" accept=".csv,.xlsx,.xls" />
Accept valueAllows
.pdfPDF files only
.pdf,.doc,.docxPDF and Word documents
image/*All image types
image/png,image/jpegPNG and JPEG only
video/*All video types
audio/*All audio types
.csv,.xlsxSpreadsheets

The accept attribute only filters the file picker dialog. Users can still bypass it by selecting “All files.” Always validate file types on the server side.

Multiple file uploads

Add the multiple attribute to allow selecting more than one file:

<label for="attachments">Attachments (up to 5 files)</label>
<input type="file" id="attachments" name="attachments" multiple accept=".pdf,.png,.jpg" />

The files are submitted as an array under the same field name. Your form backend should handle multiple files per field.

Setting file size limits

Client-side hint

Add a hidden MAX_FILE_SIZE field as a hint to the browser (not enforced by all browsers):

<input type="hidden" name="MAX_FILE_SIZE" value="10485760" /> <input type="file" name="document" />

The value is in bytes (10485760 = 10MB).

Client-side validation with JavaScript

For reliable client-side size checking:

document.querySelector('input[type="file"]').addEventListener("change", (e) => {
const maxSize = 10 * 1024 * 1024;
for (const file of e.target.files) {
if (file.size > maxSize) {
alert(`${file.name} is too large. Maximum size is 10MB.`);
e.target.value = "";
return;
}
}
});

Server-side enforcement

Always enforce file size limits on the server or form backend. Client-side checks can be bypassed.

Styling the file input

The default file input looks different across browsers. For a consistent appearance, hide the native input and use a custom button:

<label class="file-upload-btn">
<input type="file" name="document" hidden />
Choose file
</label>
<span id="fileName">No file selected</span>
<script>
document.querySelector('input[type="file"]').addEventListener("change", (e) => {
const name = e.target.files[0]?.name ?? "No file selected";
document.getElementById("fileName").textContent = name;
});
</script>
.file-upload-btn {
display: inline-flex;
align-items: center;
padding: 10px 20px;
background: #059669;
color: white;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
}
.file-upload-btn:hover {
background: #047857;
}

Drag and drop file upload

For a modern upload experience, add drag-and-drop support:

<div id="dropZone" class="drop-zone">
<p>Drag files here or click to browse</p>
<input type="file" name="document" id="fileInput" hidden />
</div>
<script>
const dropZone = document.getElementById("dropZone");
const fileInput = document.getElementById("fileInput");
dropZone.addEventListener("click", () => fileInput.click());
dropZone.addEventListener("dragover", (e) => {
e.preventDefault();
dropZone.classList.add("dragover");
});
dropZone.addEventListener("dragleave", () => {
dropZone.classList.remove("dragover");
});
dropZone.addEventListener("drop", (e) => {
e.preventDefault();
dropZone.classList.remove("dragover");
fileInput.files = e.dataTransfer.files;
});
</script>

Secure file upload handling

File uploads introduce security risks. Follow these practices:

Validate MIME types server-side

Do not trust the file extension or the Content-Type header sent by the browser. Check the actual file content (magic bytes) on the server.

Scan for malware

Uploaded files can contain malware. FormsFort direct multipart uploads enforce file rules and a limited signature check before the file is queued as an email attachment. Use the advanced object-storage path when you need ClamAV scanning and a temporary download link.

Limit file sizes

Set maximum file size limits to prevent denial-of-service attacks through large uploads.

Restrict allowed types

Only accept the file types your form actually needs. Do not allow executable files (.exe, .sh, .bat).

Store files securely

Store uploaded files outside the web root or in cloud storage with signed URLs. Never serve uploaded files directly from a public directory.

AJAX file upload with progress

For a better user experience, upload files via AJAX with a progress indicator:

document.querySelector("form").addEventListener("submit", async (e) => {
e.preventDefault();
const form = e.target;
const formData = new FormData(form);
const xhr = new XMLHttpRequest();
xhr.open("POST", form.action);
xhr.upload.addEventListener("progress", (e) => {
if (e.lengthComputable) {
const percent = Math.round((e.loaded / e.total) * 100);
document.getElementById("progress").textContent = `${percent}%`;
}
});
xhr.addEventListener("load", () => {
if (xhr.status === 200) {
document.getElementById("status").textContent = "Upload complete.";
} else {
document.getElementById("status").textContent = "Upload failed.";
}
});
xhr.send(formData);
});

Connect the upload to a static form workflow

Once the markup works, the static form backend guide explains the endpoint and delivery setup without a WordPress runtime. For a broader service-level comparison, see best form backend services. If you are specifically replacing Contact Form 7, use the Contact Form 7 migration comparison to audit mail delivery, spam controls, extensions, and cutover checks rather than treating the file input as a drop-in replacement.

Summary

To add file upload to an HTML form, use multipart/form-data encoding, add a file input, and set appropriate accept restrictions. FormsFort direct multipart uploads apply file rules and a limited signature check, then queue files as email attachments. The advanced object-storage path adds ClamAV scanning and temporary download links.

Frequently asked questions

How do I add a file upload field to an HTML form?

Set the form enctype attribute to multipart/form-data and add an input element with type='file'. The form backend must support receiving multipart submissions.

Why does my file upload not work?

The most common cause is a missing enctype='multipart/form-data' attribute on the form element. Without it, the browser sends the form as URL-encoded data and the file is not included.

How do I restrict file types in an HTML form?

Use the accept attribute on the file input to restrict the file picker to specific MIME types or extensions. For example, accept='.pdf,.doc' or accept='image/*'. Always validate file types on the server side as well.

What is the maximum file size for HTML form uploads?

HTML itself does not define a file-size limit. The limit comes from the server or form backend. For FormsFort, configure upload rules per form and plan; use a client-side hint, but always enforce the limit server-side.

Can I upload multiple files in one HTML form?

Yes. Add the multiple attribute to the file input element. The browser allows selecting multiple files, and they are submitted as an array under the same field name.

Get started free

Ready to add forms to your static site?

No backend required. Point your HTML form at FormsFort and start receiving submissions in minutes.