Fixing Rails + Carrierwave + Amazon S3 403 Forbidden Error

amazon s3 carrierwave coding software ruby on rails

So we've been using CarrierWave for a long time now for our Ruby on Rails projects. Even when we converted to direct upload, we were still using CarrierWave for image processing. It has stood the test of time and I've heard from other developers that it offers more flexibility than ActiveStorage in its current state.

The Problem

We were implementing a very basic CarrierWave solution over the past while to upload files and attach them to an active record. The requirement was that the files that were uploaded needed to be publicly visible (so default CarrierWave behaviour). It's was very standard, basic CarrierWave:

  • Create an Amazon AWS Account
  • Create an IAM User that has full access to S3
  • Create a Rails initializer to use the IAM User Keys and Fog/AWS
  • Create a CarrierWave Uploader and attach it to the ActiveRecord model
  • Add the appropriate fields to the form
  • Get the user to upload files!

All good! This is what we've been doing for years and things have been working great.

But during this implementation, we got the following error:

Excon::Error::Forbidden (Expected(200) <=> Actual(403 Forbidden)
  :body          => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>(obfuscated)</RequestId><HostId>(obfuscated)</HostId></Error>"
  :cookies       => [
  :headers       => {
    "Connection"       => "close"
    "Content-Type"     => "application/xml"
    "Date"             => "Thu, 14 Mar 2019 12:34:03 GMT"
    "Server"           => "AmazonS3"
    "x-amz-id-2"       => "(obfuscated)"
    "x-amz-request-id" => "(obfuscated)"
  :host          => "(obfuscated)"
  :local_address => "(obfuscated)"
  :local_port    => 49582
  :path          => "(obfuscated)"
  :port          => 443
  :reason_phrase => "Forbidden"
  :remote_ip     => "(obfuscated)"
  :status        => 403
  :status_line   => "HTTP/1.1 403 Forbidden\r\n"

What was going on?

Realization of the issue

So it turns out the issue has to do with the fact that what we needed and what Carrierwave by default is set up to do does not play nicely with Amazon's current default S3 settings. We needed our app to upload files and make them publicly visible. However, when you create a new S3 bucket - it is not by default set up to allow that.


So how to you fix it? You want to go to your bucket and click on the "Permissions" tab. From there you want to make sure that both "Block new public ACLs and uploading public objects" and "Remove public access granted through public ACLs" are set to false. There is a little "edit" link in the upper right to allow you to set this. You can even read the little blurb about these settings if you hover on the "i" icon when you go to edit and you'll see that these control, you'll see that these affect objects uploaded with public ACL settings.

So set those to false and that should resolve the issue.

Be careful

Now one thing I should mention is that you might want to be careful when it comes to your app and your specific security requirements. Our app was all about posting public files. If you're in a situation where you need more fine grained privacy control or are dealing with sensitive files, you might want to look into creating custom policies and custom users. The solution I've detailed here is a barebones, public file upload solution. It's also a good way to just get things going if you're in a prototyping phase or in that phase where you just needs to get things working.

Hopefully this helped some of you out.

Thanks for reading.

Casey Li
CEO & Founder, BiteSite