Describe the bug
On a Plane One (Commercial) v2.6.0 self-host installed via prime-cli --behind-proxy (bundled MinIO), all file uploads fail — project covers, profile pictures, attachments. The browser performs an S3 presigned POST /uploads (multipart/form-data) and MinIO returns:
<Error><Code>InvalidAccessKeyId</Code><Message>The Access Key Id you provided does not exist in our records.</Message><BucketName>uploads</BucketName><Resource>/uploads</Resource></Error>
The credential in the presigned form is the correct MinIO root key, and mc / boto3 calls from inside the network authenticate fine. The failure happens only through the bundled proxy-commercial Caddy.
Root cause (bisected)
The same signed presigned POST:
| Path |
Result |
→ bundled MinIO directly (any Host, incl. the public domain) |
204 No Content ✅ |
→ bundled Caddy (proxy-commercial:v2.6.0) → MinIO (with or without an upstream reverse proxy; HTTP/1.1 and HTTP/2) |
403 InvalidAccessKeyId ❌ |
MinIO never logs the request as a PostPolicyBucket op in mc admin trace — it cannot parse the multipart form, falls through to header/query auth, finds no key, and 403s. So the bundled Caddy is mangling the multipart/form-data body (or its Content-Type/framing) on the /uploads route.
Ruled out, each verified independently: the Host header (MinIO accepts the public host directly → 204), the HTTP version (h1 and h2 both 403), and any upstream reverse proxy / WAF (reproduced by hitting the bundled Caddy directly on its :80 listener, no upstream in the path).
Relevant config
The prime-cli-generated Caddy routes uploads as:
(plane_proxy) {
request_body {
max_size {$FILE_SIZE_LIMIT}
}
...
reverse_proxy /{$BUCKET_NAME} plane-minio:9000
reverse_proxy /{$BUCKET_NAME}/* plane-minio:9000
...
}
($BUCKET_NAME=uploads.) A signed POST replayed straight to plane-minio:9000 succeeds; the identical POST through this Caddy 403s.
Steps to reproduce
- Install Plane One via
prime-cli setup --behind-proxy (bundled MinIO).
- In the UI, create a project with a cover image (or upload a profile picture).
- The upload
POST /uploads returns 403 InvalidAccessKeyId.
To confirm it is the proxy: mc share upload <alias>/uploads/test.jpg to mint a presigned POST, then run the resulting curl (a) against http://<minio>:9000 → 204, and (b) against the public Plane URL → 403.
Expected behavior
Uploads through the bundled proxy succeed (204), the same as direct-to-MinIO.
Workaround
Route /uploads around the bundled Caddy — straight to MinIO — at the upstream reverse proxy (or any proxy that forwards the multipart body unmodified). That restores uploads.
Environment
- Plane One (Commercial) v2.6.0, installed via
prime-cli --behind-proxy
- Bundled MinIO
RELEASE.2025-09-07T16-13-09Z, bundled proxy makeplane/proxy-commercial:v2.6.0
- Single-host Docker
Describe the bug
On a Plane One (Commercial) v2.6.0 self-host installed via
prime-cli --behind-proxy(bundled MinIO), all file uploads fail — project covers, profile pictures, attachments. The browser performs an S3 presignedPOST /uploads(multipart/form-data) and MinIO returns:The credential in the presigned form is the correct MinIO root key, and
mc/ boto3 calls from inside the network authenticate fine. The failure happens only through the bundledproxy-commercialCaddy.Root cause (bisected)
The same signed presigned POST:
Host, incl. the public domain)proxy-commercial:v2.6.0) → MinIO (with or without an upstream reverse proxy; HTTP/1.1 and HTTP/2)MinIO never logs the request as a
PostPolicyBucketop inmc admin trace— it cannot parse the multipart form, falls through to header/query auth, finds no key, and 403s. So the bundled Caddy is mangling themultipart/form-databody (or itsContent-Type/framing) on the/uploadsroute.Ruled out, each verified independently: the
Hostheader (MinIO accepts the public host directly → 204), the HTTP version (h1 and h2 both 403), and any upstream reverse proxy / WAF (reproduced by hitting the bundled Caddy directly on its:80listener, no upstream in the path).Relevant config
The
prime-cli-generated Caddy routes uploads as:(plane_proxy) { request_body { max_size {$FILE_SIZE_LIMIT} } ... reverse_proxy /{$BUCKET_NAME} plane-minio:9000 reverse_proxy /{$BUCKET_NAME}/* plane-minio:9000 ... }(
$BUCKET_NAME=uploads.) A signed POST replayed straight toplane-minio:9000succeeds; the identical POST through this Caddy 403s.Steps to reproduce
prime-cli setup --behind-proxy(bundled MinIO).POST /uploadsreturns403 InvalidAccessKeyId.To confirm it is the proxy:
mc share upload <alias>/uploads/test.jpgto mint a presigned POST, then run the resultingcurl(a) againsthttp://<minio>:9000→ 204, and (b) against the public Plane URL → 403.Expected behavior
Uploads through the bundled proxy succeed (204), the same as direct-to-MinIO.
Workaround
Route
/uploadsaround the bundled Caddy — straight to MinIO — at the upstream reverse proxy (or any proxy that forwards the multipart body unmodified). That restores uploads.Environment
prime-cli --behind-proxyRELEASE.2025-09-07T16-13-09Z, bundled proxymakeplane/proxy-commercial:v2.6.0