Skip to main content

How to Create a File Upload Form in WordPress

Build a WordPress file upload form that accepts the right file types, states the size limit, checks the real file behind the extension, blocks scripts at the server and links each file from the submission.

A file upload form in WordPress is a form with one more field and 4 more things that can go wrong: the wrong file type gets through, the size limit is not what the help text says, a script arrives dressed as an image, or the file lands somewhere it can run. So, if you are looking to create a file upload form in WordPress, for resumes, photos, documents or design briefs, this guide can surely help.

I build Core Forms, which includes file uploads without an add-on, and every screen here comes from it running on a test site.

One guide cannot cover every kind of upload, and a form that takes 2 GB videos is a different problem from one that takes a PDF. This one covers the upload field itself, the types and the size limit, the checks that run on every file whether you asked for them or not, where the files live and how they show up in the inbox, and the mistakes that turn an upload form into a security incident.

There is nothing to download. Or, if you get stuck halfway, support is a message away. Without further ado, let’s get started.

Start From a Template or Add the Field

The Job application template ships with a resume upload already configured, and it is the fastest way to see a working upload form:

  1. Go to Core Forms > Add New.
  2. Type job in the template search and click Use this template.

For a form you already have, add the field instead:

  1. Open the form’s Fields tab and click + to open the field list.
  2. Choose File upload.
  3. Set the label, the name and whether it is required, then click Save fields.

The Core Forms builder with the field inserter open, listing the field types including File upload The inserter lists every field type. File upload is a plain field with no add-on behind it.

The builder with the Resume / CV file field selected, its help text shown as a separate HTML item beneath it, and the field's label and name in the inspector The accepted types are an accept attribute on the field. The help text under it is a separate item, so edit both when you change the types.

Choose the File Types

The accept attribute on the input lists the extensions the browser’s file picker offers, .pdf,.doc,.docx for a resume, .jpg,.jpeg,.png,.webp for photos. The inspector holds the label, the name, the required flag and the help text, and the attribute itself is edited in the Code tab, where the builder keeps it through every visual change. It is a courtesy to the visitor, not a security check, and the server does its own checking below.

The builder with the Resume / CV file field selected, showing the inspector's label, name, required and help text settings What the inspector edits. The accept and multiple attributes live on the input in the Code tab.

The plugin accepts these families out of the box:

  • Images: jpg, jpeg, png, gif, webp, avif, bmp, tif, tiff.
  • Documents: pdf, doc, docx, xls, xlsx, ppt, pptx, odt, ods, rtf.
  • Text and data: txt, log, csv.
  • Archives: zip, rar, gz, 7z.
  • Audio and video: mp3, wav, ogg, m4a, mp4, webm, mov, avi.

SVG is not on the list, on purpose. An SVG is an XML document that can carry a script, and a form that accepts logos should ask for a PNG.

Set the Size Limit and Say So

The limit is 8 MB per file out of the box, and the help text under the field is the only place a visitor learns it. Write it there: “PDF, DOC or DOCX, up to 8 MB.”

To change the limit, one filter in a small plugin or the theme’s functions file:

add_filter( 'cf_upload_max_filesize', function () {
    return 25 * 1024 * 1024; // 25 MB in bytes
} );

The server’s own limits still apply. PHP’s upload_max_filesize and post_max_size have to be at least as large as the form’s limit, or the upload fails before the plugin sees it, and the hosting control panel is where those live.

What the Visitor Sees

The field renders as the browser’s native file picker with the accepted types applied, and the chosen filename appears next to the button.

The job application form with a name, email and position filled in and a PDF chosen in the Resume / CV field, with the help text stating the accepted types and the 8 MB limit The help text is the visitor’s only warning about the limit, so keep it accurate.

A file over the limit, or of a type the server refuses, fails with a message next to the field, and the visitor’s other answers stay filled in. If any field on the form fails validation, the pending upload is discarded rather than stored, so a half-finished submission never leaves a file behind.

The Checks That Run on Every File

Five checks run on every upload, whether you configured anything or not, and they are the reason the accept attribute can be a courtesy:

Five checks on every upload: extension blocklist, double extension, real type, filename, and server-level execution block The order the checks run in. A file has to pass all 5.

  1. A blocklist of 30 or more extensions is rejected outright: php in every spelling, exe, bat, sh, cgi, pl, py, rb, jsp, asp, htaccess, svg and the rest.
  2. Double extensions are caught. report.php.jpg is refused because of the php in the middle, and data.backup.csv is fine because backup is harmless.
  3. The real file type is inspected. The file’s bytes are read to find its actual type, and a file whose content does not match its extension is rejected, so a PHP script renamed to photo.jpg never gets in.
  4. The filename is cleaned. Path characters, angle brackets, quotes and control characters are removed, spaces become hyphens, and an empty name becomes “upload.”
  5. Execution is blocked at the server. An .htaccess file in the upload folder denies script execution, and an index file prevents directory listing.

You get these by adding the field. There is no setting to turn them on, and no setting to turn them off.

Would this file get through?

Type a filename and a size. This runs the same rules the plugin applies, minus the byte inspection, which needs the file itself.

Where the Files Live

Uploads are stored under your site’s uploads folder, in a core-forms directory, in a subfolder per form, with the submission ID, the field name and a random string in the filename:

wp-content/uploads/core-forms/job-application/42_resume_6612a3b4c5d6e.pdf

Each file is also added to the Media Library as a private attachment, so it shows up in the media screen for the admin and nowhere public. If you would rather keep uploads out of the library, one filter turns that off:

add_filter( 'cf_upload_add_to_media', '__return_false' );

Read the Uploads in the Inbox

Every submission lands in the form’s Submissions tab, with the upload as a link in its own column.

Core Forms Submissions tab for the Job application form showing two applications with their positions and resume links The link opens the file. The original filename is kept in the record next to the stored one.

Click View for the full record, which holds the original name, the stored name, the size and the type. An email notification can carry the link, and a Google Sheets action or a webhook receives the file’s URL and metadata rather than the bytes.

Multiple Files

For a portfolio or a set of photos, add the multiple attribute to the input in the Code tab and give the name square brackets, name="PHOTOS[]". The visitor picks several files at once, and each one is validated on its own against the type checks and the size limit. A batch where one file fails fails as a batch, with the message naming the file, so the visitor fixes one file rather than guessing.

PHP’s max_file_uploads setting caps how many files one request can carry, and 20 is the usual default.

The Limits

The limit is per file, not per submission. Ten 8 MB files on one form is 80 MB in one request, and the server’s post size limit is the one that will stop it.

Files are stored on your server. A form that takes large videos fills the disk, and a site that expects that should send uploads to object storage through a webhook rather than keep them in the uploads folder.

There is no virus scan. The type checks stop scripts and mismatched files, and a document with a malicious macro passes as a document. Open uploads from strangers with the same care as email attachments.

Deleting a submission removes its record. The submissions inbox feature covers bulk delete, and a retention rule written above the form is the honest way to tell people how long you keep their files.

Before It Goes Live

Five things to tick off, in the browser, before the form goes on a public page:

What Quietly Ruins a File Upload Form

A help text that does not match the limit. The visitor with a 6 MB file reads “Max 5 MB,” compresses it, and you never learn the real limit was 8.

Accepting SVG for logos. It is an XML file that can carry a script, and a PNG does the job without the risk.

A required upload on a first-contact form. Plenty of people want to ask a question before they attach a document, and the upload belongs on the second form.

Leaving 3 years of resumes in the uploads folder. They are personal data, and a bulk delete once a quarter is the whole retention policy.

Final Remarks

You now have a file upload form that offers the right types, states the limit the server enforces, checks the real file behind every extension, keeps scripts from ever running and links each file from its submission. If you keep one idea from this guide, keep this one: the accept attribute is for the visitor, and the checks that matter run on the server whether you set anything or not.

The file uploads feature page has the full list of checks, and the job application form guide builds the most common upload form from its template. If something here does not behave the way this guide describes, the support team can help.

I hope the first file through it is the one you were waiting for.

FAQ

Can visitors upload files without a plugin add-on?

Yes. File uploads are part of the Core Forms license, with the type checks, the size limit and the server-level protection included, and there is no upload add-on to buy.

Where does WordPress store form file uploads?

In a core-forms folder inside the site’s uploads directory, in a subfolder per form, with execution blocked by an .htaccess file. Each file is also a private attachment in the Media Library unless you turn that off.

Can I send uploaded files to Google Drive or Dropbox?

Not directly. A webhook or a Zapier or Make action receives the file’s URL and metadata, and the automation fetches the file from that URL and stores it where you want.

Build the form. Stop reading.

Every note here came out of a real Core Forms setup. Use code CFLAUNCH for 20% off either plan. Ends September 30, 2026 (IST).

Public launch offer

20% off Core Forms

Forms, polls, surveys, payments, and licensing — every feature, unlimited sites, one plugin.

Copy your code

Those are the after-discount prices. Paste CFLAUNCH at checkout to apply it.

Ends September 30, 2026 (IST)