Putting a CDN in front of ActiveStorage

I am using DigitalOcean Spaces to store images uploaded via ActionText/ActiveStorage. This works great. I have now created a CDN (via DigitalOcean) which I’d like to put in front of my images.

My goal is for links in my ActionText to use the CDN (e.g. https://cdn.myapp.com/image-goes-here.png) rather than /rails/representations/etc links which hit my app, but I’m finding it rather unclear whether this is possible. I found this comment buried on a Rails pull request:

However, what this appears to do is replace the host name in the URLs but not the path (/rails/representations is still in the path which doesn’t work for my CDN). I could take this and hack it to remove the Rails-specific paths but this feels really dirty and wrong.

Has anyone managed to do this before and is it even possible? I’d love some advice. It feels like something that should be baked into Rails at some point.

Thanks!

I have managed to serve files uploaded through AS through CDN on Rails 5.2 and, 6.0, and 6.1. Which version are you on? Happy to provide instructions.

You could simply do this to display an image for example:

image_tag (ENV['CDN'] + talent.avatar.key)

or if it’s a variant for example:

image_tag (ENV['CDN'] + talent.avatar.variant(resize_to_fill: [260, 260], quality: "40").key)

Here ENV[‘CDN’] is an environment variable which is basically your CDN’s domain, ex:

https://yourcdn.cloudfront.net/

How to do this for images embedded in Action Text?

1 Like

I haven’t tried this myself, but in 6.1 you can use the proxy I think. This doesn’t answer your question directly, but maybe helpful:

Put this into routes:

direct :cdn_proxy do |model, options|
    if model.respond_to?(:signed_id)
      route_for(
        :rails_service_blob_proxy,
        model.signed_id,
        model.filename,
        options.merge(host: Settings.asset_host)
      )
    else
      signed_blob_id = model.blob.signed_id
      variation_key  = model.variation.key
      filename       = model.blob.filename
      route_for(
        :rails_blob_representation_proxy,
        signed_blob_id,
        variation_key,
        filename,
        options.merge(host: Settings.asset_host)
      )
    end
  end

Put this into your environment files:

config.active_storage.resolve_model_to_route = :cdn_proxy

You can call the images as:

rails_storage_proxy_url # for the main attachments
rails_blob_representation_proxy_url # for the variants

Credit: Vito Botta

Thanks for the replies, all of which I think will work with standard ActionStorage images. However, these solutions won’t work for images attached to ActionText RichText objects as far as I can tell. As @sdubois mentions, it’s unclear how to make a CDN work with ActionText.

Any ideas welcomed!

1 Like

You should be able to customize the partial for embeds to use the proxy urls, but I haven’t tried it myself. Will post a comment once I tested it.

1 Like

For me another key question is: how to add a CDN in front of Active Storage private assets (stored in a non-public S3 bucket)?

In the past I used CloudFront signed URLs which allowed to combine security and performance.

Maybe this is somehow also possible in combination with CloudFlare?

1 Like