railscloudflarer2activestorageawss3rubydebuggingwebdev

Fixing Cloudflare R2 Upload Errors with Rails ActiveStorage: The AWS SDK Checksum Problem

How a recent AWS SDK update broke R2 uploads and the complete solution to fix it.

By FadlySeptember 23, 2025

Fixing Cloudflare R2 Upload Errors with Rails ActiveStorage: The AWS SDK Checksum Problem

How a recent AWS SDK update broke R2 uploads and the complete solution to fix it


The Problem: Mysterious 500 Errors After Successful Uploads

If you're using Cloudflare R2 with Rails ActiveStorage and suddenly started getting this error after updating your gems:

Aws::S3::Errors::InvalidRequest: You can only specify one non-default checksum at a time.

You're not alone! This error started appearing for many developers in early 2025, and here's the complete story of what happened and how to fix it.

The Mystery Deepens

The frustrating part was that the error occurred after successful uploads:

✅ S3 Storage (316.5ms) Uploaded file to key: abnadmtk12fq0x9g8f55p0ttmitu (checksum: qWd7UPgFQd39JfP+nykRHg==) ❌ Completed 500 Internal Server Error in 429ms ❌ Aws::S3::Errors::InvalidRequest: You can only specify one non-default checksum at a time.

The file would upload successfully to R2, but then Rails would crash with a 500 error. Users would see a broken experience despite the upload actually working.

The Root Cause: AWS SDK's New Default Integrity Protections

After digging through the AWS SDK S3 Ruby changelog, I discovered the culprit:

AWS SDK S3 Ruby version 1.178.0 (released January 15, 2025) introduced a breaking change:

  • Feature - This change enhances integrity protections for new SDK requests to S3. S3 SDKs now support the CRC64NVME checksum algorithm, full object checksums for multipart S3 objects, and new default integrity protections for S3 requests.

  • Feature - Default to using CRC32 checksum validation for S3 uploads and downloads.

The AWS SDK now automatically calculates and sends CRC32 checksums with every S3 request. While this works perfectly with real AWS S3, Cloudflare R2's S3 compatibility layer doesn't support multiple checksums, causing the error.

The Complete Solution

Here's the comprehensive fix that resolves all checksum-related issues with R2:

1. Update Your Storage Configuration

In config/storage.yml, add checksum disabling options to your R2 configuration:

# Cloudflare R2 Configuration (S3-compatible) r2: service: S3 access_key_id: <%= ENV['R2_ACCESS_KEY_ID'] %> secret_access_key: <%= ENV['R2_SECRET_ACCESS_KEY'] %> region: auto bucket: <%= ENV['R2_BUCKET_NAME'] %> endpoint: <%= ENV['R2_ENDPOINT'] %> force_path_style: true # 🔧 Disable checksums for R2 compatibility upload: checksum_algorithm: ~ # 🔧 AWS SDK client configuration http_wire_trace: false compute_checksums: false

2. Configure Production Environment

In config/environments/production.rb, use proxy mode for better R2 compatibility:

# Store uploaded files on Cloudflare R2 config.active_storage.service = :r2 # 🔧 Use proxy mode for better R2 compatibility # This serves files through Rails instead of direct R2 URLs config.active_storage.delivery_method = :proxy

3. Create R2 Compatibility Initializer

The most important part - create config/initializers/active_storage_r2.rb:

# config/initializers/active_storage_r2.rb # R2 compatibility fixes for ActiveStorage Rails.application.configure do config.after_initialize do if Rails.application.config.active_storage.service == :r2 Rails.logger.info "🔧 Applying R2 compatibility patches for ActiveStorage" # Disable analysis for R2 - this prevents checksum operations ActiveStorage::Attached::One.class_eval do def analyze_later Rails.logger.info "Skipping ActiveStorage analysis for R2 compatibility" end def analyze Rails.logger.info "Skipping ActiveStorage analysis for R2 compatibility" end end ActiveStorage::Attached::Many.class_eval do def analyze_later Rails.logger.info "Skipping ActiveStorage analysis for R2 compatibility" end end ActiveStorage::Blob.class_eval do def analyze_later Rails.logger.info "Skipping blob analysis for R2 compatibility" end def analyze Rails.logger.info "Skipping blob analysis for R2 compatibility" end end Rails.logger.info "✅ R2 compatibility patches applied successfully" end end end

4. Update Your Environment Variables

Make sure your .env.production includes all necessary R2 credentials:

# Cloudflare R2 Configuration R2_ACCESS_KEY_ID=your_r2_access_key_id R2_SECRET_ACCESS_KEY=your_r2_secret_access_key R2_BUCKET_NAME=your_r2_bucket_name R2_ENDPOINT=https://your_account_id.r2.cloudflarestorage.com

Why This Solution Works

The fix addresses three different sources of checksum operations:

  1. AWS SDK Level: compute_checksums: false disables the new default CRC32 checksums
  2. ActiveStorage Level: checksum_algorithm: ~ disables Rails' own checksum calculations
  3. Analysis Level: The initializer prevents ActiveStorage from analyzing uploaded files, which also triggers checksum operations

Testing Your Fix

Create a simple test to verify everything works:

# lib/tasks/test_r2.rake namespace :r2 do desc "Test R2 upload functionality" task test_upload: :environment do puts "🧪 Testing R2 Upload..." service = ActiveStorage::Blob.service test_content = "Test upload at #{Time.current}" test_key = "test/upload_test_#{Time.current.to_i}.txt" service.upload(test_key, StringIO.new(test_content), checksum: nil) puts "✅ Upload successful" downloaded = service.download(test_key) puts "✅ Download successful: #{downloaded}" service.delete(test_key) puts "✅ Cleanup completed" puts "🎉 R2 is working correctly!" rescue => e puts "❌ Error: #{e.message}" end end

Run it with:

rails r2:test_upload

The Results

After implementing this solution:

  • No more 500 errors after uploads
  • Files upload successfully to R2
  • No checksum conflicts
  • Better performance with proxy mode
  • Future-proof against AWS SDK updates

Why Cloudflare R2 Has This Limitation

Cloudflare R2 implements S3 API compatibility but doesn't support all S3 features. The checksum validation system is one area where R2's implementation differs from AWS S3. While AWS S3 can handle multiple checksum algorithms simultaneously, R2 currently supports only one checksum at a time.

This isn't necessarily a limitation - it's a design choice that keeps R2 simpler and more cost-effective while still providing excellent S3 compatibility for most use cases.

Affected Versions

This issue affects:

  • AWS SDK S3 Ruby: 1.178.0+ (January 2025 onwards)
  • Rails ActiveStorage: All versions when used with R2
  • Cloudflare R2: All versions (due to API compatibility limitations)

Alternative Solutions

If you prefer not to disable checksums entirely, you could:

  1. Downgrade AWS SDK: Pin to version 1.177.0 or earlier
  2. Use different storage: Switch to AWS S3 for uploads requiring checksums
  3. Manual checksums: Implement your own checksum validation logic

However, the solution above is the most comprehensive and maintains all the benefits of the latest AWS SDK while ensuring R2 compatibility.

Conclusion

The AWS SDK's move to default integrity protections is generally a good thing - it makes S3 uploads more reliable and secure. However, it exposed compatibility differences between AWS S3 and Cloudflare R2.

By understanding the root cause and implementing the comprehensive solution above, you can continue using R2's cost-effective storage while maintaining compatibility with the latest AWS SDK versions.

The key lesson here is that S3-compatible services aren't always 100% compatible with every S3 feature, and staying on top of dependency updates is crucial for maintaining a stable application.

Tags

#rails #cloudflare #r2 #activestorage #aws #s3 #debugging #webdev #ruby

FAQ

Why do uploads succeed but Rails returns a 500 error afterward?

Because the file uploads to R2 successfully, but when Rails finalizes the operation, the AWS SDK sends checksum headers R2 cannot accept, triggering an error on completion.

Which AWS SDK S3 versions are affected?

Version 1.178.0 and newer (January 2025 onwards) enabled default checksum behavior that triggers this issue on R2.

Can I use direct-uploads with R2 after this fix?

Yes. With checksums disabled and ActiveStorage analysis skipped for R2, direct uploads should work without checksum conflicts.

Will this impact performance?

Disabling checksums may marginally improve performance due to fewer integrity calculations. Using proxy delivery may add minimal overhead but improves compatibility.