Cloudfront Static Site

S3 is a really basic service that allows us to store files in the cloud. Cloudfront is a more advanced service that allows us to take those files stored in an S3 bucket and distribute them globally.

If our static files exist in a bunch of locations around the world, this will significantly reduce latency when people request our files. On top of that we get a lot more power over cloudfront than we do s3 and we can add custom domain names and ssl certificates. We can also run lightweight serverless functions in cloudfront, but that's a topic for another day.

We are going to host a static website using S3 and Cloudfront.

Here's what we'll do:

  • Create another S3 Bucket to store the files
  • Create a Cloudfront Distribution to serve the files from the bucket
  • Enable website hosting in cloudfront
  • Add a dns record to point to the cloudfront distribution
  • Setup a certificate to use https

Static Site Requirements

You can use any static site you want for this, it just has to be valid HTML and optionally css, javascript, images, and other assets.

If you wan't, you can use this qr code generator as your static site. Just download the dist.zip folder and unzip it. It contains the index.html file and other assets. Feel free to clone the react code and play around with the app if you like.

step 1:

Create a new S3 bucket in your playground account and upload your website files to it.

This is different than the steps we went through to host a static website in an S3 bucket. It's just a simple S3 bucket with the files stored it it.

  • It doesn't matter what you name the bucket, as long as it's unique
  • You don't need to configure any special permissions on the bucket
  • You don't need to enable web hosting on the bucket

You can do this through the AWS console, or you can use the AWS CLI. If you run the following script, make sure you change the four variables at the top.

Show timestamps
00:00
Now, we're going to use CloudFront to host a static website, which comes with a lot of benefits
00:04
over just hosting through S3. We'll be able to get a TLS connection for better security, and
00:09
it’ll also be distributed globally, cached at all the edge locations, and we’ll use the
00:14
AWS backbone network. So it's a much better and more performant way to distribute a static
00:19
website than just using an S3 bucket. So let's get started. I'm
00:23
going to open up my playground account in AWS.
00:26
The first thing we're going to do before we use CloudFront is
00:30
set up our main infrastructure in a single region.
00:33
Since this is a static website—static assets—I'm still going to need an S3 bucket that's
00:39
in a single region and has all of my static assets in it. That's where they'll live. That's the source
00:44
of truth. Then we'll set up CloudFront to use that as its origin,
00:48
read those assets, distribute them globally, and cache them at the edge locations.
00:53
So we'll start with S3. Because these are static assets, we're going to create a brand new bucket
00:57
for this. Don’t use the previous static site buckets. We're going to create a brand new one
01:02
because it needs different rules and permissions than the other ones. So this is going to be a
01:05
general-purpose bucket. I can name this whatever I want, so I'm actually going to do this as, uh,
01:10
the QR code static site again. So I'll call this 'QR code' but for CloudFront. And then
01:16
everything else we're going to leave the same. Because, like I said before, in AWS, most of the time
01:20
things are going to be locked down so that nothing can access it until we give it permission.
01:25
So nothing is going to have access to this S3 bucket until we say it can. So we're going to block
01:29
public access, leave all the default settings on, and then we'll create that bucket.
01:33
The next thing we're going to do is upload the static site to this bucket, just like we
01:38
did for S3 before. So I'm going to open up this bucket and upload the files for
01:43
the QR static site. You can use any static site you want, but I'll use this QR one. So
01:48
I'm going to download this 'dist' folder and drag in the contents—not the
01:53
folder itself. I want index.html to be in the root of this bucket, which will make it a little
01:57
bit easier. So I'll drag those in—I’ve got index.html, I’ve got some JavaScript, CSS—and
02:03
then we'll upload these files. Now, with S3, like I said, nothing has
02:07
access to the objects in this S3 bucket unless they're given permission.
02:12
I'm logged in as an admin user, so I can access these objects in the bucket. But nobody
02:17
else is able to access this S3 bucket, and that's the way we want it. Nothing should have
02:22
access until we give it permission. So when we set up the CloudFront distribution, we
02:25
will give it permission to access these objects. But right now, it's completely locked down and
02:28
private unless you're logged in as an admin like I am. It's worth noting
02:32
that I did this in us-west-2, so this is located in Oregon.
02:35
So if someone tried to connect to this S3 bucket from anywhere else in the world that
02:39
isn't close to us-west-2, there would be some latency. So we're going to reduce
02:43
that by adding a CloudFront distribution that will distribute it globally.
02:47
Now, I'll head over to the CloudFront dashboard, and we're going to create a new
02:53
distribution. Notice with CloudFront, we don't create it in a single region—you can't select
02:57
a region for this. It's a global service, and there are a few global services in AWS where you
03:03
don't select a region. CloudFront is the main one. Just by setting this up, we're going to get
03:07
access to all of those edge locations and the AWS backbone. So I'm going to create a new CloudFront
03:11
distribution. This is for a single website or app. And immediately, we need to select an origin.
03:16
Because, like I said, CloudFront doesn't actually host our assets. We still have them stored
03:20
in a region somewhere—CloudFront is really just for distributing our content.
03:24
So I am going to use the 'qr-bucket-for-cloudfront'—that's the new bucket I made. Make sure you select
03:28
the correct bucket. Then scroll down to origin access. Here is where we're
03:33
going to specify the permissions for how CloudFront is able to access the S3 bucket.
03:39
The bucket is not public—instead, we're selecting origin access control settings. So
03:44
now we're specifying permissions so that the S3 bucket can only
03:50
be accessed by this CloudFront distribution. Since we don't have an origin access control created,
03:55
we're going to create a new one and CloudFront will do this for us. We can leave all
04:00
the settings as default because that is the most secure most of the time. So we'll create that.
04:06
Now it's saying it will provide us with a policy statement after we create the distribution,
04:10
and then we'll have to update S3 later to make sure it has those permissions.
04:15
This way, we're limiting S3 access only to this CloudFront distribution. We
04:19
don't need to tweak any of these settings for a basic website. We'll enable compression and
04:26
redirect HTTP to HTTPS so people always use an HTTPS—TLS connection. Since this is a static
04:32
site, we really only need GET and HEAD. We don't want to restrict viewer access since we want anyone
04:38
on the public internet to be able to access this as a public website. But this could be handy for
04:43
something like a social media or chat app where users could upload static assets like images, but
04:48
you’d only want to allow authorized people to view those. For a static website, we
04:53
want anyone to be able to view our website, so we're not going to restrict access. We'll
04:57
leave cache settings as the default. Right here we could associate
05:04
CloudFront Functions—these are lightweight JavaScript functions—but we don’t
05:08
need any of that right now. We could get more security and pay more money by using WAF, but
05:13
we're not going to do that. It does cost a little bit, whereas CloudFront for what we're using it
05:17
for right now will be completely free. We don't need any static IP addresses; we can limit this
05:23
to certain edge locations like just North America or Europe, but we'll leave all edge locations on.
05:29
Now we'll create an alternate domain name, because we really don't want
05:34
one of those ugly cloudfront.amazonaws.com URLs for our application.
05:40
So we're going to add an item here. Again, I'm opening up Route 53 in a new tab just
05:45
so we always have that as a reference, since this needs to be a subdomain of a domain
05:51
you have set up in Route 53. I have this as my root domain, so
05:56
I’ll just copy this to make sure I don't get it wrong. Go back to CloudFront here, and an
06:00
alternate domain name can be any subdomain. So I'll do cloudfront-static-site
06:07
.play. I don't know why it's giving me an error there—maybe that'll go away.
06:11
Now we're going to have a custom SSL certificate. Since we have a custom domain, we need an
06:15
SSL certificate for it. We'll have to request a certificate. Again, this is free
06:20
with CloudFront, but we need to enable this TLS certificate now. So I'll
06:26
paste that domain, and I'm going to copy it from here again: cloudfront-static-site.play.
06:31
cloudcourse.xyz. We want a unique domain name for each infrastructure
06:36
piece. It's giving me an error because there's a space—yeah, spaces at the end. Stupid spaces! Okay.
06:41
Now we have a valid domain. I'm in Certificate Manager, so I just clicked on 'Request
06:46
Certificate', and that's it for the domain name. We're going to use DNS validation since we have
06:49
Route 53 set up; this is the easiest way of doing it. We can leave the other settings
06:53
as default and request this new certificate from AWS. This should take,
06:59
I don't know, maybe a couple minutes at most. It's saying pending validation. I often find
07:03
that if I refresh this after about a minute, it still hasn't changed yet—because we have to
07:07
create the records in Route 53. Down here where it says 'Pending validation', you need
07:11
to click 'Create records in Route 53'. You just need to do this once and then create the records.
07:16
As long as you entered a subdomain of your domain in your hosted zone, it will allow you to
07:22
create records in Route 53. If you don't see this, you might have a typo in your domain.
07:26
Now this is pending validation. If I refresh this after a couple of minutes, this
07:30
should be valid, but we do need to wait for it before we can update the certificate
07:34
in CloudFront. Okay, so I just refreshed again—status is 'Success,' so that's good. Now, over here,
07:39
if I click the little refresh button on the right side, I should see my
07:44
certificate. We're going to use TLS 1.2, which is the most recent version we have access
07:50
to. So, now we have the TLS certificate and alternate domain name. Down here for
07:55
the default object—since this is a static website—we need to make sure that index.html is
08:02
always served up as the default object
08:05
from the S3 bucket. We can enable IPv6—that's fine.
08:09
We could set up logging, but we'll get more into logging in a later section.
08:13
That looks good, so we're going to create this distribution. Nope—not yet! You have to say
08:20
'Do not enable security protections.' Again, you could, but this just costs extra money, so
08:23
I'm not doing it. So we'll scroll down and create the distribution. CloudFront
08:27
distributions usually take a couple of minutes to be completely set up since it’s deploying
08:32
globally and setting up all the edge locations.
08:36
This will take a little while, but while it's happening, we can go set up the Route 53
08:41
domain name for the CloudFront distribution, since we still need to set those records in
08:46
Route 53. If we go create a record now—and I'm still in the wizard view, because it's
08:50
easier when working with services like CloudFront—we'll set up a simple routing record
08:54
and go next, define a simple record. I'll paste that in—just the subdomain, so
09:00
cloudfront-static-site, that's what I’ve been using for my CloudFront distribution. We're going to
09:03
set up an A record for CloudFront and we'll select the CloudFront static
09:10
site—the one we just created—and define the simple record. Then we define another one.
09:15
Yep, click that. It's going to be the exact same thing, but instead of an A record, it's going to be an AAAA
09:21
record for IPv6 as well. We'll select CloudFront distribution and define the simple record.
09:26
Now we should have these two records set up so we can use that domain name with
09:29
CloudFront. Then I'll click 'Create records.' So now I have my CloudFront static site
09:33
records, I have the CNAME record for the CloudFront certificate, and I also have my A
09:39
record from one of the previous things we did with the S3 bucket.
09:44
If I head back to distributions, it's probably still creating—yeah, it's still
09:47
deploying over here. This can take a little while, but something else we need to do is still give
09:53
permission to CloudFront to access that S3 bucket. Remember, we have to
09:58
give S3 a policy that allows it to communicate with CloudFront—or, rather, allows CloudFront
10:02
to get the objects from the bucket. There was a little pop-up that I exited
10:07
that had the policy we needed. I'll show you how to get that if you close out of the pop-up. So
10:12
we'll go to 'Origins.' CloudFront always has origins where it accesses the
10:15
original content from. Here is the bucket that's its origin. If I click 'Edit,' we're
10:19
not actually going to edit this, but if I scroll down... I know we can copy that policy. So it's
10:24
accessing that bucket. Here is the policy we need. I'll copy that. Now we need to head
10:29
over to the bucket in S3. So I'll open up S3, and
10:34
go to the bucket that's selected as the CloudFront origin.
10:38
Then we go over to Permissions, scroll down to 'Bucket policy,' and
10:42
edit the policy to say who can access objects within this bucket. I'm just going to paste that
10:48
policy that I copied—it's just a JSON policy again. We can see that we are
10:52
allowing CloudFront to get objects from this bucket. That's all it's doing, so that's
10:58
fine. We'll save those changes. Now it should be able to access the contents from S3.
11:04
Without that, nothing can access the S3 bucket, so our CloudFront distribution would just not
11:07
work. Now if I head back to CloudFront—I'll get out of here, I'm not actually going to make any
11:11
changes—I'll just go back to the distribution, and it's still deploying. It can take a while,
11:15
but there's still one more thing I want to do here. Let's go to error
11:20
pages in the CloudFront distribution and create a custom error response, because
11:24
this static website is a single-page application. It’s using React, but it could be any
11:31
SPA. If there’s an error trying to retrieve the site, like if you go to a path that doesn't
11:35
exist, instead of having CloudFront deliver a generic 404 or something like that, we
11:40
want to handle that client side in the SPA. We'll have a little bit of JavaScript
11:44
client-side logic that can show a nice 404 page or whatever else we want.
11:48
We're actually going to select the 403 Forbidden, because if we try to request an
11:51
object that doesn't exist, CloudFront will actually give us a Forbidden error. So we're going to
11:56
set up a custom response for that. Create custom error response—and I got too click-happy there,
12:00
we were not done setting it up yet, so I'll edit this. We need to customize the error response.
12:05
For the response code, we're going to give a 200 OK, and the path/page is going to be
12:10
/index.html, because everything is going to redirect to our index.html, and we'll have our
12:16
client-side JavaScript handle it. This is something you want to do if you have an SPA that
12:21
you're trying to serve through CloudFront. I'll save these changes, and now if I go back
12:25
to General, it's still deploying—this really is taking a while! So it might just need a couple
12:31
more minutes and then it should be ready to test. Actually, while I'm waiting, I'll show you
12:36
how you would update assets in CloudFront. If I wanted to update my static
12:40
website that's hosted in S3, I'd just go to my S3 bucket,
12:45
into my objects, and update index.html, JavaScript, CSS, whatever—and
12:50
that's fine. As soon as people start making requests to S3, the new versions of the
12:55
files will be served up. But because CloudFront is caching these files at the edge locations,
13:00
it’s going to have a cached version of my index.html. If I make an update, the cached
13:05
version might still be alive for another 24 hours or so, and people
13:10
making requests to my site might still see the old version of a static asset.
13:14
If we update our assets here, we first have to update the origin, which is the
13:19
S3 bucket. I could make a change to my website and re-upload the files here—that’s all fine.
13:23
That's the first step. But the second step, if you want people to see the new files immediately through CloudFront...
13:28
because it's cached, you have to go into CloudFront, go to the Invalidations tab, and
13:34
create an invalidation. You're invalidating the cache of that object—forcing
13:40
CloudFront to go make a request back to the origin S3 bucket, get the new file,
13:45
and then cache that new file instead. So I could just invalidate everything—'/*'—if I
13:50
wanted to, or just invalidate index.html, whatever makes sense for your
13:56
app. But if you need CloudFront to grab the newest version of something, you have to invalidate
14:02
the path for that object. Okay! Finally it’s not deploying anymore, which means we
14:07
should be able to visit the domain name. That's going to take me to the certificate page, but I can still
14:13
copy the domain name. Let's see—cloudfront-static-site.play
14:18
.cloudcourse.xyz. This is using HTTPS.
14:21
That's great. That's better than just the S3 bucket. The site does seem to be working. Like I
14:25
said, it's just a static site, so there's nothing dynamic here—just a QR code generator.
14:30
We could do a speed test or something just to
14:35
see the difference between it loading here and halfway around the world.
14:38
But generally, when you use CloudFront, it's going to be much faster—it’ll be
14:42
distributed globally, and it's just a much better way to serve up static assets. I can go to
14:46
different parts of the site, like 'My Codes,' and because I set up that redirect to index.html,
14:50
this is just going to work. I think it’s pretty easy to set up a static site with CloudFront,
14:55
and you get a lot of benefits doing it this way. But it's not super simple—there's
15:00
a lot of steps, and a lot of buttons to click in the dashboard.
15:03
And generally, if I'm setting up a static site with CloudFront—which I do
15:06
a lot—I'm never going to do this manually. I'm always going to automate it with scripts.
15:10
And it's also dirt cheap, by the way. I don't know the exact prices, but I think there's
15:14
a ridiculous CloudFront free tier. Yeah, you get a lot of data—a lot of bandwidth in the
15:18
free tier. I don't know the exact stats—I just clicked on a blog post. But it is very
15:23
cheap. This is going to be free for us—we’re not going to spend any money. If you were streaming
15:28
4K video to a lot of users, you’d want to monitor your bill a little bit, but for
15:32
general static assets, it’s a really good way of distributing them. Talking about automation,
15:37
there are a couple of tools. There’s one by AWS called CloudFront Hosting Toolkit, where
15:41
you go through, run a couple of commands, and it will set up CloudFront to
15:45
pull your code from GitHub and update your static site every time it changes—every time the main
15:51
branch changes. Or you could automate this with a Bash script or Python script or
15:55
something. My favorite is always using SST for this. SST is an
15:59
infrastructure as code tool. They have an AWS construct for static sites, and
16:04
they also have built-in options for frameworks like Next.js, Solid, and others. But essentially, it
16:11
gets to a point, if you’re doing this a lot, where you’ll end up using these tools.
16:17
For me, setting up a static site with CloudFront is as simple as creating a file with
16:21
this amount of code in it and then just running 'deploy'. It’s really simple—we’re
16:27
learning all the manual steps here, but eventually you’ll be automating these things
16:31
and it becomes a trivial task. But we’ll get more into infrastructure as code and
16:35
all these things later on in the course. For now, we’re just going to keep doing everything manually through the web console in
Cloud Course
Cloud Course
$89.70
$299.00 Lifetime
  • 81+ learning resources
  • 57 lessons and tutorials
  • 15 hands-on deployments
  • 9 quizzes
  • 29 videos (4h 17m 38s)
  • More content coming soon
  • Unlimited lifetime access to all course content
  • Deployment assessment CLI tool
  • Exclusive Discord access
Original Price:$299.00
Discount:-$209.30
Total:$89.70