Attachment patterns, variants
Lessons from 37signals' Fizzy codebase.
Use preprocessed: true to prevent on-the-fly transformations failing on read replicas:
has_many_attached :embeds do |attachable|
attachable.variant :small,
resize_to_limit: [800, 600],
preprocessed: true
end
Centralize variant definitions in a module.
Problem: When using Cloudflare (or similar CDN/proxy), large file uploads can fail with signature expiration errors. Cloudflare buffers the entire request before forwarding it to your origin server. For large files on slow connections, this buffering can take longer than Rails' default signed URL expiry (5 minutes), causing the upload to fail even though the user is still actively uploading.
Solution: Extend the direct upload URL expiry to accommodate slow uploads:
# config/initializers/active_storage.rb
module ActiveStorage
mattr_accessor :service_urls_for_direct_uploads_expire_in,
default: 48.hours
end
# Prepend to ActiveStorage::Blob
def service_url_for_direct_upload(expires_in: ActiveStorage.service_urls_for_direct_uploads_expire_in)
super
end
Why 48 hours? This provides ample time for even the slowest uploads while still expiring unused URLs. The signed URL is single-use anyway, so the security impact is minimal.
Skip previews above size threshold:
module ActiveStorageBlobPreviewable
MAX_PREVIEWABLE_SIZE = 16.megabytes
def previewable?
super && byte_size <= MAX_PREVIEWABLE_SIZE
end
end
blob.variant(options)blob.preview(options)Don't conflate them - different operations.
Problem: Streaming avatar images through your Rails app ties up web workers and adds latency. Every avatar request occupies a Puma thread while bytes flow through.
Solution: Redirect to the blob URL and let your storage service (S3, GCS, etc.) serve the file directly:
def show
if @user.avatar.attached?
redirect_to rails_blob_url(@user.avatar.variant(:thumb))
else
render_initials if stale?(@user)
end
end
Key details:
:thumb variant to avoid on-the-fly transformationsstale? to the initials fallback, not the redirect—otherwise browsers will show broken images after an avatar change until the cache expiresPattern: Use Active Storage's mirror service to write to multiple backends simultaneously while reading from a fast local primary.
Use cases:
# config/storage.yml