Admin panel always sends multipart/form-data even when no file upload is present in the form

Open
#17,777 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
72/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
react, typescript
Domain
frontend

Research direction

Start with @payloadcms/ui/dist/elements/SaveDraftButton/index.js, PublishButton/index.js, and Autosave/index.js, tracing how each request payload and content type are constructed. Verify the JSON-only collection workflow and a workflow containing File or Blob data; done means JSON-only submissions use application/json while file uploads continue to use multipart/form-data.

Written by the indexing model from the issue text.

Description

area: ui invalid-reproduction status: needs-triage v3
Describe the Bug

The admin panel unconditionally sends all PATCH/POST requests (save, autosave, publish, save draft) as multipart/form-data, even when the request payload contains only JSON data and no file is being uploaded.

Per HTTP semantics, multipart/form-data is intended for requests that include binary file data. When the payload is purely JSON (no File or Blob objects in the FormData), the request should use application/json. Sending JSON-only data wrapped in multipart encoding is unnecessary overhead and triggers false positives in standard WAF configurations.

Verified behavior:

  • Exact same JSON body sent as application/json to the same endpoint: 200 OK
  • Same JSON body wrapped in multipart/form-data to the same endpoint: 403 blocked by WAF

The issue is that the admin panel always constructs a FormData object and appends the JSON as a field named "_payload", regardless of whether any File objects are present.

The affected components in @payloadcms/ui/dist/elements/ are:

  • SaveDraftButton/index.js
  • PublishButton/index.js
  • Autosave/index.js

Expected behavior: When the form submission does not include any File or Blob objects, the request should be sent as application/json with the JSON body directly, not wrapped in multipart/form-data.

Link to the code that reproduces this issue

https://github.com/payloadcms/payload/tree/main (reproducible with any collection behind Cloudflare Enterprise WAF)

Reproduction Steps
  1. Create a collection with multiple fields (no upload/image fields)
  2. Deploy behind Cloudflare Enterprise with Managed Ruleset active
  3. Open admin panel, create or edit a document, click Save
  4. The browser sends a PATCH request with content-type: multipart/form-data
  5. Cloudflare WAF blocks the request with 403 (rule 6179ae15870a4bb7b2d480d4843b323c, score-based)
  6. Send the exact same body manually with content-type: application/json — it passes with 200
Which area(s) are affected?

area: ui

Environment Info
Binaries:
  Node: 24.14.0
  npm: 11.9.0
  Yarn: N/A
  pnpm: 9.7.0
Relevant Packages:
  payload: 3.87.0
  next: 15.3.9
  @payloadcms/db-mongodb: 3.87.0
  @payloadcms/db-postgres: 3.87.0
  @payloadcms/drizzle: 3.87.0
  @payloadcms/email-nodemailer: 3.87.0
  @payloadcms/graphql: 3.87.0
  @payloadcms/live-preview: 3.87.0
  @payloadcms/live-preview-react: 3.87.0
  @payloadcms/next/utilities: 3.87.0
  @payloadcms/payload-cloud: 3.87.0
  @payloadcms/plugin-cloud-storage: 3.87.0
  @payloadcms/plugin-form-builder: 3.87.0
  @payloadcms/plugin-nested-docs: 3.87.0
  @payloadcms/plugin-redirects: 3.87.0
  @payloadcms/plugin-search: 3.87.0
  @payloadcms/plugin-seo: 3.87.0
  @payloadcms/richtext-lexical: 3.87.0
  @payloadcms/storage-s3: 3.87.0
  @payloadcms/translations: 3.87.0
  @payloadcms/ui/shared: 3.87.0
  react: 19.1.0
  react-dom: 19.1.0
Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.3.0: Wed Jan 28 20:54:46 PST 2026; root:xnu-12377.91.3~2/RELEASE_ARM64_T6000
  Available memory (MB): 32768
  Available CPU cores: 10
Dominant language
TypeScript
Stars
44.8k
Forks
4.2k
Avg merge
2d 21h
Merged PRs (30d)
53

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from payloadcms/payload

All issues in payloadcms/payload

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.