Skip to content

Send Content-MD5 header on S3 uploads#728

Merged
m90 merged 1 commit intooffen:mainfrom
tianshanghong:fix/s3-object-lock-content-md5
Feb 18, 2026
Merged

Send Content-MD5 header on S3 uploads#728
m90 merged 1 commit intooffen:mainfrom
tianshanghong:fix/s3-object-lock-content-md5

Conversation

@tianshanghong
Copy link
Contributor

@tianshanghong tianshanghong commented Feb 17, 2026

S3 buckets with default Object Lock retention reject PutObject requests that lack a Content-MD5 (or x-amz-checksum-*) header, returning HTTP 400 InvalidRequest.

This PR sets SendContentMd5: true in the minio-go PutObjectOptions so the SDK computes and sends the header automatically.

The overhead is negligible (one MD5 hash per upload) and the header doubles as a server-side integrity check for all uploads, not just Object Lock buckets.

Fixes #727

Set SendContentMd5: true in PutObjectOptions so that minio-go
includes a Content-MD5 header with every PutObject request.

AWS S3 requires this header when the target bucket has Object Lock
enabled; without it the upload is rejected with HTTP 400
(InvalidRequest).  The overhead is negligible — one MD5 hash per
upload — and the header also serves as a server-side integrity
check for all uploads regardless of Object Lock.

Fixes offen#727
@m90
Copy link
Member

m90 commented Feb 17, 2026

Thank you, this looks good. One question before merging: I remember running into a newer version of aws-cli adding this feature, which broke usage against a very old self-hosted MinIO instance (this happened in a different context than this tool). However, the tests pass for this change, and they also run against something ancient (August 2020).

Can we be entirely sure this works against all S3 compatible backends? Or would it be required to make this an option?

@tianshanghong
Copy link
Contributor Author

Thanks for the careful review! Great question. I looked into this and I believe Content-MD5 is safe across all S3-compatible backends.

Content-MD5 is the oldest integrity mechanism in the S3 spec. It has been part of the API since the beginning, and every S3-compatible backend I'm aware of supports it, including MinIO, which has handled the header since at least May 2016.

The breakage you're remembering was likely from the newer checksum headers (x-amz-checksum-algorithm, CRC64NVME, CRC32C). Most notably, the February 2025 AWS SDK change defaulted to CRC64NVME and broke MinIO, Cloudflare R2, Backblaze B2, and others.

In minio-go, SendContentMd5: true tells the SDK to use the traditional Content-MD5 header rather than the newer x-amz-checksum-* scheme. This is actually the more compatible choice. Several projects (Gitea, Thanos, Grafana Mimir) explicitly set this flag as a compatibility fix after minio-go v7.0.37 started defaulting to the newer checksums (minio/minio-go#1791).

For a tool that needs to work with any S3-compatible backend, Content-MD5 is the safest option.

@m90 m90 merged commit 9644083 into offen:main Feb 18, 2026
3 checks passed
@m90
Copy link
Member

m90 commented Feb 18, 2026

This is now released in v2.47.1, thanks for your contribution 🎩

@tianshanghong tianshanghong deleted the fix/s3-object-lock-content-md5 branch February 18, 2026 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

S3 uploads fail with HTTP 400 on buckets with Object Lock enabled

2 participants