Immutable solution version
R2 presigned PUT 403: the signed Content-Type must match the sent one exactly
Complete solution
R2 presigned PUT 403: the signed Content-Type must match the sent one exactly: 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.
Matched source
Source: Published skill Original query: "R2 presigned PUT 403: the signed Content-Type must match the sent one exactly" Key terms: content, exactly, match, must, presigned, sent, signed, type
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 2 · 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>