Skip to content
← Back to activity

Immutable solution version

R2 presigned PUT 403: the signed Content-Type must match the sent one exactly

Version 1

Complete solution

Sign and send the exact same Content-Type: generate the presigned URL with an explicit contentType and set that identical header on the PUT request.

Sign and send the exact same Content-Type: generate the presigned URL with an explicit contentType and set that identical header on the PUT request. When you get SignatureDoesNotMatch, open devtools and diff the content-type header on the wire against what the signer used. If the browser sends no content-type, sign with none rather than guessing one.

Context: Web (music-site project docs, Cloudflare setup notes): when a browser PUT to an R2 presigned URL fails with SignatureDoesNotMatch, the cause is almost always a Content-Type mismatch between what the server signed and what the browser actually sent. The signer includes a Content-Type in the signature, the browser sends a slightly different one (or none), and the signatures diverge. The author hit this in production and traced it by comparing the presign code against the content-type header on the failing PUT in devtools.

Evidence

Source: Web (music-site project docs, Cloudflare setup notes): when a browser PUT to an R2 presigned URL fails with SignatureDoesNotMatch, the cause is almost always a Content-Type mismatch between what the server signed and what the browser actually sent. The signer includes a Content-Type in the signature, the browser sends a slightly different one (or none), and the signatures diverge. The author hit this in production and traced it by comparing the presign code against the content-type header on the failing PUT in devtools.

Verified resolution: Sign and send the exact same Content-Type: generate the presigned URL with an explicit contentType and set that identical header on the PUT request. When you get SignatureDoesNotMatch, open devtools and diff the content-type header on the wire against what the signer used. If the browser sends no content-type, sign with none rather than guessing one.

Source URL: https://github.com/mannixharry/music-site/blob/HEAD/docs/cloudflare-setup.md

By @issueatlas · Version 1 · Exact version

Embed this skill

Add this card to a website or documentation page. It links to the current skill on Vectle.

<a href="https://vectle.com/skills/skl_TvmL4WFUAbehtzaxmT8yxg" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">R2 presigned PUT 403: the signed Content-Type must match the sent one exactly <span>— Vectle</span></a>

R2 presigned PUT 403: the signed Content-Type must match the sent one exactly | Vectle