DevelopmentDecember 20, 2025

Deploying a static site with 99.9% uptime using Cloudflare and AWS S3

Welcome to this simple guide for anyone looking for 99.9% uptime and an almost free deployment solution for their static sites, without having to set up or maintain their own server. In this article we'll do it with Cloudflare as the CDN and AWS S3 as file storage.

Cloudflare has more than 150 data centers spread around the world, and its Basic (free) plan is more than enough for most sites. Even though its features are basic, you'll still get 99.9% uptime, a free SSL certificate, DDoS protection, Brotli compression, automatic JS/CSS minification, hiding of your origin server's IP, and proper cache configuration.

We decided to write this article because most guides out there overwhelm you with pointless settings that don't make any sense and look copied from other sites.

Prerequisites

  • An AWS account
  • A Cloudflare account with your NS already migrated

Migrating to Cloudflare is very simple, because it has a very polished UI/UX. It scans your DNS records and suggests adding the ones that already exist.

Pros and cons

✅ Free high-performance CDN;

✅ 99.9% uptime;

✅ Free SSL certificate;

✅ Simple setup and use;

❌ You can't use S3 with Cloudflare if your site's name is already taken as a bucket name (we explain this below);

❌ On the free plan, https sites are only supported on top-level subdomains.

1. Create the S3 bucket

Go to S3 from the AWS console and click "Create bucket."

On the next screen, choose the bucket name and region. The bucket name must match your site's name. This is an AWS requirement, and if you don't follow it, you won't be able to access your site through Cloudflare. The region doesn't matter, since Cloudflare caches the site anyway.

From that screen on, click "Next" until the bucket is created. You don't need to change anything else in these settings. Note: if you get the "Bucket name already exists" message at this step, check the "Troubleshooting" section at the end of the article.

2. Create the IAM user

First you need to install the AWS command-line tool following this link. Once you have it ready, go to the IAM tab in AWS, then Users, then Add user. Make sure to check Programmatic access, since this user doesn't need any permissions beyond publishing the site's content.

By default, the user is created with no additional permissions. Just click "Next" until you reach the "Success" page with the user's credentials

Download the .csv and store it somewhere safe. You'll need this data for the AWS CLI.

3. Assign the right permissions to the user

Once the user is created, click on it and, in the permissions tab, click "Add inline policy."

You'll see the policy generator page, with the following settings:

Actions: in the Write section, PutObject and DeleteObject; in the Permissions section, PutObjectAcl

Resources: your bucket's ARN, which looks like this: arn:aws:s3:::test.example.com/*

In the JSON view it should look like this

Review the policy and give it a name. With this, the permissions are set up and you can configure aws-cli to upload your site.

4. Deploy with AWS CLI

Open the terminal and run aws configure. It will ask for the credentials from the .csv file you downloaded when creating the IAM user. Enter your Access Key ID and your Secret Access Key. The rest of the fields are optional, and you can just press "Enter."

Now you can deploy your site with the AWS CLI. Go to your site's folder, which should contain index.html and the rest of the files. Then run:


aws s3 cp {folder name} s3://{bucket name} — recursive — acl public-read

Real example:


aws s3 cp dist/ s3://test.example.com — recursive — exclude ‘.DS_Store’ — acl public-read

With this command we copy the folder to the remote S3 bucket.

  • — recursive copies all nested folders;
  • with — exclude you can define lists of files that don't need to be copied, like temporary, IDE, or OS files;
  • — acl public-read is needed so your site is accessible.

We use copy instead of sync because sync doesn't behave as you'd expect, and some files end up not being copied to the bucket even though they were modified.

5. Publish the site

To finally publish the site, go to the S3 bucket list, click on yours, and open the Properties tab. Enable static website hosting and set index.html as the index document. Copy the site's URL and click "Save."

The last step is to go to Cloudflare's DNS settings and add a new CNAME record. As mentioned earlier, the bucket name has to match the CNAME. In Cloudflare's domain name field, put the link to your site on S3 without "http" or "/", like this: test.example.com.s3-website-us-east-1.amazonaws.com. In the name field, put your site's name without the domain. For example, for "test.example.com," the name would just be "test."

And that's it. Your site should now be accessible from the domain you chose.

Troubleshooting

  • Make sure you have "Flexible" mode enabled in Cloudflare's SSL section; otherwise, you might run into SSL certificate issues.
  • To redirect from the www domain to the non-www domain, make sure to do it as a top-level subdomain (Cloudflare limitation). You can do this from Cloudflare's Page Rules section or by creating a new bucket with a redirect rule in S3.
  • If you get the "Bucket name already exists" error when creating the bucket, this solution won't work for you, since in that case you won't be able to use Cloudflare.

Tell us about your case

Get in touch