Building a Safe WordPress File Upload Form
A file upload field turns a contact form into a way for strangers to put files on your server. Here is how to set types and size limits, where the files end up, and how to keep them private.
Some links on this page are affiliate links: if you buy through one, we may earn a commission at no extra cost to you. It does not change what we recommend; see the editorial policy.
The short version: a WordPress file upload form needs four decisions made explicitly — which file types, what maximum size, where the files are stored and who can reach them by URL — because the defaults of most form plugins make uploads publicly downloadable.
Adding the field takes a minute in any decent form builder. The work is in what happens to the file after the visitor presses submit.
1. Allow only the types you need
Every type you allow is a type someone can send you. Start from the job:
| Job | Allow | Do not allow |
|---|---|---|
| CVs, applications | pdf, docx |
zip, exe, html |
| Photos for a quote | jpg, png, webp, heic |
svg, zip |
| Design or print files | pdf, ai, eps (if you need them) |
php, js, html |
Set the list in the field itself, not just in the help text. WordPress also keeps its own list of permitted MIME types; if a legitimate type is refused even though the form allows it, upload_mimes explains how that list works and how to extend it narrowly.
Avoid SVG and HTML unless you have a specific reason: both can carry script that runs in the browser of whoever opens them.
2. Set the size limit in the right places
Three limits apply, and the smallest one wins:
- The field's own maximum in the form plugin.
- PHP's limits —
upload_max_filesizefor a single file andpost_max_sizefor the whole submission, which must be larger. - The web server's request limit, such as
client_max_body_sizeon nginx (1 MB by default, which produces a "413 Request Entity Too Large" error).
The PHP side, in php.ini or your host's equivalent:
upload_max_filesize = 20M
post_max_size = 25M
max_input_time = 120
max_input_time covers the time spent receiving the upload. For fields that accept several files, max_file_uploads (20 by default) caps the count per request.
post_max_size needs headroom over the file size because it covers every field plus encoding overhead, and several files in one submission add up. If visitors see "the uploaded file exceeds the upload_max_filesize directive", work through that error — and set the form's own limit a little below PHP's, so the visitor gets a friendly message instead of a server error.
3. Know where the files are stored
Most form plugins save uploads in a subfolder of wp-content/uploads. That directory is served directly by the web server, so a file there is usually downloadable by anyone who has, or guesses, its URL.
Check what your plugin does:
- Randomised file names or folders make URLs hard to guess. That is obscurity, not access control, but it helps.
- A
.htaccessfile denying access in the upload folder works on Apache and LiteSpeed. On nginx it is ignored completely; the equivalent rule must go in the server config. - Storage outside the web root or in private cloud storage is the strongest option, and few plugins offer it by default.
Test it: submit a file, copy its URL from the entry, and open it in a private window while logged out. If it downloads, assume anyone could.
4. Treat every upload as hostile
The real risk is a file the server will execute, or one a person will open.
- Never allow server-side script types —
php,phtml,pharand friends. Checking the extension alone is not enough; good plugins also check the file's actual content type. - Block execution in the upload folder. On Apache, a rule in that folder's
.htaccess(see the default .htaccess file for how they are structured); on nginx, a location block placed above the generic PHP one, since the first matching regex wins:location ~* ^/wp-content/uploads/.*\.(php|phtml|phar)$ { deny all; }. - Scan if you accept files from the public at volume. Some hosts scan uploads; otherwise a security plugin can.
- Open received files carefully. Office documents with macros are a classic delivery route, and they target whoever opens the attachment, not the server.
This belongs alongside the rest of the security checklist. If an upload form has already been abused, malware removal covers the clean-up.
5. Email the file or email a link?
| Attach to email | Link to entry | |
|---|---|---|
| Reliability | Large mail is often rejected | Small notification, delivered |
| Security | Unencrypted copy in every inbox | One copy, behind login |
| Deletion | Copies live on in mailboxes | Delete once, gone |
| Convenience | Open directly | Requires admin login |
Links are the better default. Attachments are a common cause of notifications that never arrive; if yours do not, see contact form not sending email.
6. Retention, deletion and privacy
Uploaded files are frequently personal data — CVs, ID photos, medical letters.
- Decide how long you keep them and delete on schedule, not "when the disk fills up".
- Make sure deleting an entry deletes its file. In some plugins it does not, and orphaned files remain in the uploads folder.
- Exclude or encrypt them in backups if backups are stored off-site.
- Mention uploads in the privacy notice, per GDPR compliance.
Choosing a plugin
Many commercial builders keep the upload field behind a licence, while some free plugins include one. In WPForms the file upload field is in every paid tier, with per-field extension, size and file-count settings. Uploads go to wp-content/uploads/wpforms by default, but the field has an option to restrict file access to specific users or a password — turn it on for anything personal. Choosing to store files in the Media Library does the opposite and puts them in the public library. Free alternatives exist; the comparison is in contact form plugins.
Common mistakes
- Allowing "all file types" to stop support requests.
- Raising
upload_max_filesizebut notpost_max_size. - Trusting
.htaccessprotection on an nginx server. - Attaching every upload to the notification email.
- Deleting entries but leaving the files behind.
Verify
- Upload an allowed file at just under the limit — it should succeed.
- Upload one just over the limit — you should see the form's message, not a server error.
- Rename a
.txtfile to.phpand try to upload it — it must be rejected. - Open an uploaded file's URL logged out, in a private window, and confirm the result matches your intent.
- Delete a test entry and confirm its file disappears from the server.
Frequently asked
- Most form plugins save them in a subfolder of wp-content/uploads, sometimes with randomised names. That folder is web-accessible by default, so anyone with the URL can download the file unless the plugin or server blocks direct access.
- Three limits apply and the smallest wins: the field's own maximum in the form plugin, PHP's upload_max_filesize and post_max_size, and sometimes the web server's request size limit. Raising one without the others changes nothing.
- Only small, non-sensitive ones. Attachments bloat mail, get rejected by size limits and sit unencrypted in inboxes indefinitely. Sending a link to the file in the admin area is more reliable and keeps one copy you can delete.