How to Add File Uploads to WordPress Forms (Without Getting Burned)

File uploads are one of those form features that seem trivial until you actually ship one. A job application form needs a résumé field. A support form is ten times more useful with a screenshot attached. A photo contest doesn’t work at all without one. And yet upload fields are also the single most dangerous thing you can bolt onto a WordPress form: done carelessly, they hand strangers a way to place arbitrary files on your server, leak personal documents through guessable URLs, and quietly wreck your email deliverability. In this guide we’ll walk through how upload fields work in the major form builders, and — more importantly — the safety checklist we run before any upload form goes live.
When you actually need file uploads
Before adding an upload field, it’s worth being honest about whether you need one, because every upload field is attack surface. The use cases where they genuinely earn their keep:
- Job applications: résumés and cover letters (
.pdf,.docx). This is the classic case — asking applicants to email documents separately kills your completion rate. - Support and bug reports: screenshots and log files. A single
.pngof the error usually saves three rounds of back-and-forth. - Photo and design submissions: contests, print orders, listings, insurance claims — anywhere the file is the submission.
- Verification documents: proof of purchase, ID checks. These carry the heaviest privacy obligations, which we’ll get to.
If a plain text field would do the job — “paste a link to your portfolio” instead of “upload your portfolio” — take the text field. Fewer moving parts, fewer risks.
How upload fields work in the major form builders
All the big builders can accept files, but the experience ranges from “drag a field in” to “read the documentation twice.”
Contact Form 7
CF7 has a [file] tag, and it works — but it demands care. You configure allowed extensions and a size limit directly in the tag (for example [file your-file filetypes:pdf|docx limit:2mb]), and if you skip those constraints you inherit whatever the server allows. By default CF7 stores uploads in a temporary directory, attaches them to the notification email, and deletes the file afterwards — so if the email fails, the file is simply gone. There’s no submission storage in core; most CF7 upload setups need an add-on to keep files at all. Workable, but it’s the setup where we see the most mistakes.
WPForms and Gravity Forms
Both have polished, native upload fields with type restrictions, size limits, and multi-file support — drag-and-drop in the modern styles. The catch is pricing: WPForms gates the file upload field behind its paid tiers (it is not in WPForms Lite), and Gravity Forms has no free version at all, so uploads start at their entry-level licenses. If uploads are your whole reason for choosing a builder, factor that in.
Fluent Forms
Fluent Forms handles uploads with sensible controls — files stored with submissions, per-field type and size restrictions — though the upload field has historically sat on the Pro side of the fence.
Trinity Forms
Full disclosure: Trinity Forms is our own plugin, so weigh our praise accordingly — but the relevant fact here is simple and verifiable: the file upload field is in the free tier, with per-field type allowlists, size limits, and files stored with the submission rather than only emailed. We put it in free because we think “accept a résumé” shouldn’t be a paywall feature.
Upload features at a glance
| Builder | Upload field in free version? | Type/size restrictions | Files stored with submissions? |
|---|---|---|---|
| Contact Form 7 | Yes, via [file] tag |
Manual, in the tag — easy to get wrong | No — emailed then deleted (add-on needed) |
| WPForms | No — paid tiers only | Yes, per field | Yes (paid) |
| Gravity Forms | No free version | Yes, per field | Yes |
| Fluent Forms | Upload field is Pro | Yes, per field | Yes (Pro) |
| Trinity Forms | Yes — free tier | Yes, per field | Yes |
If you’re still deciding between builders more broadly — beyond just uploads — our form builder comparison covers the full feature-and-pricing picture.
The safety checklist
This is the part that separates a fine upload form from a security incident. Run through every item before launch.
1. Restrict file types — and mean it
Allowlist the handful of extensions you actually need (.pdf, .docx, .jpg, .png) rather than blocklisting bad ones. Two types deserve special paranoia:
- Never allow
.php(or.phtml,.php5, or anything the server might execute). An executable file inside your web root is a full site takeover waiting for someone to guess the URL. - Treat
.svgas dangerous. SVGs are XML and can carry embedded JavaScript — a stored-XSS vector the moment anyone views the file in a browser. If you must accept them, sanitize them server-side; if you don’t have a sanitizer, don’t accept them.
Also check that your builder validates the file’s actual content (MIME sniffing), not just the extension — renaming shell.php to shell.jpg is the oldest trick in the book.
2. Cap the file size
Set a per-field limit that matches the use case — 2–5 MB is plenty for a résumé, maybe 10 MB for photos. This isn’t just about disk space: oversized uploads time out on slow connections (a terrible applicant experience) and are an easy denial-of-service lever. Remember the effective ceiling is the lowest of your field limit, upload_max_filesize, and post_max_size in PHP.
3. Mind where the files land
Most builders drop uploads somewhere under wp-content/uploads/, which is publicly readable by design. That means anyone with the URL — or a directory listing, or a lucky guess — can fetch someone’s résumé. Good builders mitigate this with randomized, unguessable file paths; better ones store files outside the web root or protect the directory. Check where yours puts files and verify that browsing to the folder doesn’t list its contents.
4. Serve files through authenticated downloads
The gold standard: files are never fetched by raw URL at all. Instead, the plugin streams the file through a download endpoint that checks you’re a logged-in admin first. Obscured paths are a decent fallback; an access check is the real fix, because unguessable URLs still end up in email threads, logs, and browser histories.
Uploads are personal data — plan for GDPR
A résumé is about as personal as data gets: name, address, employment history, sometimes ID numbers. Under GDPR (and similar laws) you need a lawful basis for collecting it, a retention policy for deleting it, and the ability to hand it over or erase it on request. In practice that means: decide up front how long uploaded files live (e.g. “applications deleted 6 months after the role closes”), actually automate that deletion, and mention uploads explicitly in your privacy notice. We’ve written a fuller walkthrough of making WordPress forms GDPR-compliant — everything there applies double to forms that collect files.
Don’t attach big files to notification emails
The tempting default — “email me the file” — is a deliverability trap. Mail servers commonly reject attachments over 10–25 MB, large attachments raise spam scores, and once a file is forwarded around inboxes you’ve lost all control over that personal data (see the GDPR point above). The better pattern: store the file with the submission and put a link in the notification email — ideally one that requires an admin login to open. Your emails stay small, deliverable, and the file stays in one governed place.
Uploads as a spam vector
Bots love upload fields. Some spam campaigns exist purely to park malware or SEO junk on other people’s servers; others attach garbage files to inflate your storage or ride your domain’s reputation. The standard anti-spam stack — honeypots, token checks, and a modern CAPTCHA on high-risk forms — applies here just as it does everywhere else; our guide to stopping WordPress form spam covers the full toolkit. Two upload-specific additions: review uploaded files before trusting them anywhere (never auto-publish user uploads), and consider requiring the rest of the form to validate before the upload is accepted at all.
The bottom line
File uploads are worth having — for applications, support, and submissions they can double the usefulness of a form. But treat every upload field as a small security project: allowlist types, ban .php and unsanitized .svg, cap sizes, verify where files land, serve them through an access check, set a retention policy, and link rather than attach in emails. Ten minutes of checklist now beats an incident report later.
Frequently asked questions
Can I add file uploads to WordPress forms for free?
Yes, but options are limited. Contact Form 7’s file tag is free but stores nothing by default, and WPForms and Gravity Forms gate uploads behind paid licenses. Trinity Forms includes a file upload field with type and size restrictions in its free tier, with files stored alongside submissions.
What file types should I never allow in a WordPress upload form?
Never allow executable types like .php, .phtml, or .php5 — an executable file in your web root can lead to a full site takeover. Treat .svg as dangerous too, since SVGs can contain embedded JavaScript, and only accept them if they are sanitized server-side. Allowlist only the types you need, such as .pdf, .docx, .jpg, and .png.
Should form uploads be attached to notification emails?
No, not for anything sizable. Large attachments get rejected by mail servers, raise spam scores, and spread personal data into inboxes you can’t control. Store the file with the form submission and put a link in the notification email instead — ideally one that requires an admin login to download.