Laravel: Serving Amazon S3 Assets Through AWS CloudFront

Laravel - Amazon CloudFront - Amazon S3

Using a Content Delivery Network (CDN) to serve your website's static assets is a great way to improve your website speed.

While there are many CDN services out there, AWS CloudFront is an easy choice if you are already using other AWS services like S3 (Simple Storage Service) or EC2 (Elastic Compute Cloud). Also, Amazon CloudFront is affordable and reliable.

In this post, I will show you how to serve your Laravel assets that are hosted in an S3 bucket through AWS CloudFront.

To follow along with this tutorial, you will need an active AWS account. If you don't have one, you can register for free through their free tier program.

Let's dive in.

Step 1. Setting Up An S3 Bucket

If you already have an S3 bucket, you can skip this step.

  1. Login to your AWS account, open the S3 console, and

  2. Choose Create bucket.

    The Create bucket wizard opens.

  3. In Bucket name, enter a name for your bucket.

    For this tutorial, we will name our bucket laravel-s3-cloudfront.

  4. In the Region field, choose the AWS Region where you want the bucket to reside.

    Choose a Region close to you to minimize latency, costs and address regulatory requirements.

    I'm going to choose US East (Ohio) us-east-2 for the sake of this tutorial.

    Amazon S3 Bucket name and region
  5. In Bucket settings for Block Public Access, uncheck Block all public access, and check the "I acknowledge that the current settings might result in this bucket and the objects within becoming public" checkbox.

    AWS S3 Block Public Access settings for this bucket setting

  6. You can use the default option for the rest.

  7. Scroll down and click Create bucket.

    AWS S3 Create bucket button

Step 2. Configure S3 Bucket policy

Once your S3 bucket has been created, the next step is to configure your bucket policy.

To be able to use a CloudFront distribution with your S3 bucket, your bucket policy must allow s3:GetObject to public users. That is, you must allow public access to the files inside the bucket.

Follow these steps to configure your bucket policy for s3:GetObject:

  1. Click on the name of your S3 bucket from the Amazon S3 console, to open it, and,

  2. Choose the Permissions tab.

    AWS S3 Bucket Permissions Tab
  3. Scroll to the Bucket Policy section and click the Edit button.

    AWS S3 Bucket Policy Edit button
  4. In the Policy input field, paste the following code;

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "Allow-Public-Access-To-Bucket",
          "Effect": "Allow",
          "Principal": "*",
          "Action": [
            "s3:GetObject",
            "s3:GetObjectVersion"
          ],
          "Resource": "arn:aws:s3:::laravel-s3-cloudfront/*"
        }
      ]
    }

    Notice how the value of the Resource property is composed using the Bucket ARN displayed on top of the editor. Be sure to replace the bucket name in there if you are using a different name for your bucket.

    S3 Edit bucket policy
  5. Scroll down the page and click the Save changes button.

Step 3. Setting up a CloudFront Distribution

To set up Amazon CloudFront distribution to serve your assets from S3;

  1. Open the CloudFront console, and

  2. Click the Create Distribution button.

    Amazon CloudFront - Get started
  3. Under Web, click the Get Started button.

    Amazon CloudFront - Web - Get started
  4. For the Origin Domain Name field, choose the S3 bucket we created above from the drop-down menu.

  5. For Viewer Protocol Policy, choose HTTP and HTTPS.

  6. You can use the default option for the rest.

    Amazon CloudFront, Create Distribution
  7. Scroll down the page and click the Create Distribution button.

  8. Once the distribution has been created, click on the ID to open up the details page, and make note of the Distribution domain name. We will use it later.

    CloudFront Distribution details

Step 4. Set Up AWS Access Keys

Now you need to set up an IAM user on AWS to allow your Laravel application to be able to upload and access files on your S3 bucket. You can of course skip this step if you already have an IAM user set up.

  1. Open the IAM console

  2. Click Users on the left side of the page, and

  3. Click the Add Users button on the right side of the page

    AWS IAM Add user
  4. For the User name, I am going to choose laravel-cloudfront. You can choose any user name that makes sense for your situation.

  5. For the Select AWS access type options, I am going to only check the Programmatic access checkbox, since I will only be using this user's credentials from the Laravel Application.

  6. Click the Next: Permissions button.

    AWS IAM - Add user - Set User details
  7. On the next page, Click the Attach existing policies directly tab and type S3 in the search input.

  8. Check the AmazonS3FullAccess checkbox and click Next: Tags

    AWS IAM - Add user - Set permissions
  9. On the next page, simply click the Next: Review button, since we don't want to add any tags in this tutorial.

    AWS IAM - Add user - Add tags
  10. Review the user details on the next page, and then click the Create user button.

    AWS IAM - Add user - Review
  11. Copy the Access key ID and the Secret access key to a safe place, we will need them shortly.

    AWS IAM - Add user - Keys

Step 5. Configure The Laravel Project

Before you can use the S3 driver in Laravel, you need to install the league/flysystem-aws-s3-v3 package via Composer. You should also install a cached adapter for increased performance:

composer require --with-all-dependencies league/flysystem-aws-s3-v3 "^1.0"

composer require league/flysystem-cached-adapter "~1.0"

S3 Driver Configuration

Next, take a look at the S3 driver configuration information located in your config/filesystems.php configuration file, from your Laravel root folder.

's3' => [
    'driver' => 's3',
    'key' => env('AWS_ACCESS_KEY_ID'),
    'secret' => env('AWS_SECRET_ACCESS_KEY'),
    'region' => env('AWS_DEFAULT_REGION'),
    'bucket' => env('AWS_BUCKET'),
    'url' => env('AWS_URL'),
    // ...
]

As you see here, most of the configuration information is loaded from environmental variables. So, instead of modifying the configuration file directly, go ahead and update the .env file with the details from the previous steps.

The configuration options you need to update are;

  • AWS_ACCESS_KEY_ID which you get from step 4 above, item number 11

  • AWS_SECRET_ACCESS_KEY: which you also get from step 4, item 11

  • AWS_DEFAULT_REGION from step 1, item 4

  • AWS_BUCKET: the name of your bucket from step 1, item 3. And

  • AWS_URL: Your CloudFront domain from step 3, item 8. Be sure to prepend it with https://

If any of these keys are not already in your .env file, simply add them.

AWS_ACCESS_KEY_ID=[Your-Access-key-ID]
AWS_SECRET_ACCESS_KEY=[Your-Secret-access-key]
AWS_DEFAULT_REGION=us-east-2
AWS_BUCKET=laravel-s3-cloudfront
AWS_URL=https://[Your-CloudFront-Distribution-Domain]

Serving Laravel Assets from CloudFront

Now, in order to make sure your assets that are uploaded to your S3 bucket are served from your CloudFront distribution, you need to use the Storage Facade’s url() method to output the asset URLs.

For example, if you upload an image named my-photo.jpg to your S3 bucket, to display the image in your Blade template you should do it like this;

<img src="{{ Storage::disk('s3')->url('my-photo.jpg') }}" alt="My Photo" />

Using a Custom Domain With AWS CloudFront

Check out How To Use a Custom Domain With AWS CloudFront In Laravel.

And that's it for this post.

Questions?

If you have any questions, drop them in the comment section below and I will do my best to answer them.

Also, consider following me on Twitter @nzesalem.✌🏽