<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ cloudfront - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ cloudfront - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 30 May 2026 16:31:40 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/cloudfront/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Host a Static Website on AWS S3 and CloudFront ]]>
                </title>
                <description>
                    <![CDATA[ DevOps might seem like a complex field with various specializations and tools. In this article, I’ll simplify one key aspect by demonstrating how to host a static website using Amazon S3 (Simple Storage Service) and CloudFront, AWS’s Content Delivery... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/host-a-static-website-on-aws-s3-and-cloudfront/</link>
                <guid isPermaLink="false">67e2cd0841be93b03e257b93</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ S3 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cloudfront ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud Computing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oghenekparobo Stephen ]]>
                </dc:creator>
                <pubDate>Tue, 25 Mar 2025 15:34:32 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742916792332/9dbf6a18-7260-434f-815d-e38a82c9e47e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>DevOps might seem like a complex field with various specializations and tools. In this article, I’ll simplify one key aspect by demonstrating how to host a static website using Amazon S3 (Simple Storage Service) and CloudFront, AWS’s Content Delivery Network (CDN) that caches and distributes content efficiently for faster access.</p>
<p>To follow along, you should have:</p>
<ul>
<li><p>An <a target="_blank" href="https://aws.amazon.com/free/"><strong>AWS account</strong></a><strong>.</strong></p>
</li>
<li><p>A basic understanding of AWS architecture (You can gain insights <a target="_blank" href="https://youtu.be/NhDYbskXRgc?si=mZi-dN4AbdVZtWD5">here</a>.)</p>
</li>
<li><p>Knowledge of <a target="_blank" href="https://aws.amazon.com/iam/"><strong>AWS IAM</strong></a> (Identity and Access Management for secure resource access control)</p>
</li>
<li><p>Familiarity with <a target="_blank" href="https://youtu.be/RGOj5yH7evk?si=AXlPrk1czT9zm4ql"><strong>Git and GitHub</strong></a></p>
</li>
</ul>
<p>Let’s dive in and set up our static site hosting step by step.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-aws-s3">What is AWS S3?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-fine-grained-access-controls-in-s3">Fine-Grained Access Controls in S3</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-encryption-at-rest-in-s3">Encryption at Rest in S3</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-upload-a-static-website-to-amazon-s3">How to Upload a Static Website to Amazon S3</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-a-bucket">What is a Bucket?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-bucket">How to Create a Bucket</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-upload-files-and-folders-to-an-s3-bucket">How to Upload Files and Folders to an S3 Bucket</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-permissions-and-properties-in-aws-s3">How to Set Permissions and Properties in AWS S3</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-a-bucket-policy">What is a Bucket Policy?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-a-bucket-policy">How to Set Up a Bucket Policy</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-enable-static-hosting">How to Enable Static Hosting</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-amazon-cloudfront">Amazon CloudFront</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-key-features-of-amazon-cloudfront">Key Features of Amazon CloudFront</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-amazon-s3-alone-is-not-enough">Why Amazon S3 Alone is Not Enough</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-you-should-serve-your-website-with-cloudfront">Why You Should Serve Your Website with CloudFront</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-a-cloudfront-distribution">What is a CloudFront Distribution?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-understanding-the-amazon-resource-name-arn">Understanding the Amazon Resource Name (ARN)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-updated-s3-bucket-policy">Updated S3 Bucket Policy</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-what-is-aws-s3">What is AWS S3?</h2>
<p>Amazon Simple Storage Service (Amazon S3) is an object storage service designed to store and retrieve any amount of data from anywhere.</p>
<p>Using Amazon S3 is straightforward. You start by selecting a region, creating a storage container called a "bucket," and then uploading your data. There is no limit to how much data you can store, and you can retrieve it at any time.</p>
<p>Amazon S3 automatically creates backups of your data by storing copies across multiple devices. It also allows you to preserve different versions of your files, helping you recover data if it’s accidentally lost. If you delete a file by mistake, you can restore it using Amazon S3’s versioning feature.</p>
<p>Amazon S3 offers configurable lifecycle policies to help manage data efficiently throughout its lifecycle. Security is a top priority for AWS, ensuring that data uploads and retrievals are protected using SSL encryption for secure transmission. AWS also provides multiple security features to safeguard your data, including fine-grained access controls and encryption at rest. Let’s explore these two features in a bit more detail.</p>
<h3 id="heading-fine-grained-access-controls-in-s3"><strong>Fine-Grained Access Controls in S3</strong></h3>
<p>Amazon S3 provides fine-grained access control, allowing you to define who can access your data and what actions they can perform. This is managed through:</p>
<ol>
<li><p><strong>AWS Identity and Access Management (IAM)</strong>: Controls user permissions at the AWS account level. You can grant specific users or roles permissions to access S3.</p>
</li>
<li><p><strong>S3 bucket policies</strong>: JSON-based policies applied at the bucket level to control access for all objects in a bucket.</p>
</li>
<li><p><strong>Access Control Lists (ACLs)</strong>: Defines permissions for individual objects within a bucket (less commonly used since bucket policies are more powerful).</p>
</li>
<li><p><strong>Block public access settings</strong>: Prevents accidental public exposure of S3 data by restricting open access.</p>
</li>
</ol>
<h3 id="heading-encryption-at-rest-in-s3"><strong>Encryption at Rest in S3</strong></h3>
<p>Encryption at rest ensures that stored data remains secure, even if unauthorized access occurs. S3 supports multiple encryption options:</p>
<ol>
<li><p><strong>Server-Side Encryption (SSE):</strong></p>
<ul>
<li><p><strong>SSE-S3:</strong> AWS manages encryption keys automatically.</p>
</li>
<li><p><strong>SSE-KMS:</strong> Uses AWS Key Management Service (KMS) for additional control over encryption keys.</p>
</li>
<li><p><strong>SSE-C:</strong> Customers provide their own encryption keys.</p>
</li>
</ul>
</li>
<li><p><strong>Client-Side Encryption:</strong></p>
<ul>
<li>Data is encrypted <strong>before</strong> being uploaded to S3 using customer-managed encryption keys.</li>
</ul>
</li>
</ol>
<p>We could spend endless time researching and exploring the theory behind Amazon S3, but practical application solidifies learning. Now, let’s move on to uploading a static website to Amazon S3.</p>
<p>As mentioned earlier, you should have a basic understanding of how AWS works, including signing up, signing in, and creating IAM users. This is crucial because we will use an IAM user to perform operations securely. Understanding Identity and Access Management is essential in AWS, as it ensures proper access control and security while managing resources.</p>
<h2 id="heading-how-to-upload-a-static-website-to-amazon-s3">How to Upload a Static Website to Amazon S3</h2>
<p>To upload a static website, you first need a static site. If you do not have one, you can use a free template from <a target="_blank" href="https://www.free-css.com/free-css-templates">Free CSS</a>.</p>
<p>I’ve also provided a ready-to-use template that you can clone from this GitHub repository: <a target="_blank" href="https://github.com/Oghenekparobo/Mediplus-free-template">Mediplus Free Template</a>.</p>
<p>Now that your static project is ready, let’s go to AWS and upload it to an Amazon S3 bucket.</p>
<p>Login to your AWS account using your IAM user credentials. Once logged in, you will be redirected to the AWS Management Console.</p>
<p>Your AWS Dashboard should look similar to this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742473569107/436e229b-f16d-4572-b26c-b2359d2ef738.jpeg" alt="AWS User or IAM user dashboard" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Navigate to the S3 service by clicking on the S3 link in the AWS dashboard. If you don’t see it, which is unlikely, use the search bar at the top of the dashboard. Simply type "S3", and it will appear in the results. Click on it to proceed.</p>
<p>Once you arrive at the Amazon S3 page, you will see the Create Bucket button.</p>
<p>S3 buckets are the backbone for many applications, including content delivery, data backup, archiving, static website hosting, and big data storage for analytics.</p>
<h2 id="heading-what-is-a-bucket">What is a Bucket?</h2>
<p>Amazon S3 buckets are the fundamental storage containers within AWS Simple Storage Service that provide secure, scalable repositories for digital assets.</p>
<p>Each bucket features a globally unique name, regional deployment, and flat object storage architecture identified by unique keys.</p>
<p>With 99.999999999% durability through built-in redundancy, S3 buckets support crucial infrastructure needs including content distribution, data archiving, and static website hosting. Admins can implement comprehensive data governance through configurable access controls (policies, ACLs, IAM), lifecycle management, versioning capabilities, and encryption protocols to meet organizational security requirements.</p>
<h3 id="heading-how-to-create-a-bucket">How to Create a Bucket</h3>
<p>To create a bucket, click on the "Create bucket" button. You will then be redirected to the bucket creation page.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742476384065/3a7df14d-2c5c-4169-98e9-473e7d96590b.jpeg" alt="create bucket page" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Choose any name you like, as long as it follows AWS bucket naming rules.</p>
<p>You will also see various configuration options, but for now, leave them as default. We will make the necessary adjustments later in the project.</p>
<p>Finally, click on "Create bucket", and just like that your bucket is created! You should now see your bucket, which acts as a container for storing your files or data:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742477392285/409692a6-7838-42d4-bc61-e691b0d41477.jpeg" alt="409692a6-7838-42d4-bc61-e691b0d41477" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-upload-files-and-folders-to-an-s3-bucket">How to Upload Files and Folders to an S3 Bucket</h3>
<p>Now, let’s upload the static site we created or <a target="_blank" href="https://github.com/Oghenekparobo/Mediplus-free-template">cloned</a> to our S3 bucket.</p>
<p><strong>1. Click on your bucket:</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742479043138/19d5ee6b-67b2-422e-a1d5-e021d0248ef3.jpeg" alt="highlighting the bucket to click" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>After clicking on your bucket, you will be taken to the bucket details page, where you can manage various settings and configurations. This page provides options to adjust permissions and properties, monitor metrics, manage access points, and upload files using the "Upload" button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742479342643/deec0a64-6c4b-4670-8c38-e9949c18a93e.jpeg" alt="created bucket page or bucket  info page" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><strong>2. Upload project files:</strong></p>
<p>If your static site project is ready, it should contain the essential files needed for deployment. While the structure may vary, it should include an index.html file, along with necessary assets such as images (or an image folder), CSS files (or a CSS folder), and JavaScript files (or a JS folder) to ensure proper functionality and styling.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742479661115/367b7346-3762-4405-a612-fd5cb0d59094.jpeg" alt="project or static site files" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Let’s start by uploading the necessary files according to our project structure. Start with the root-level files such as index.html, along with any other essential files on the root-level. Ensure you follow your project structure carefully and upload all required files first to maintain proper structure.</p>
<p>Then click on the upload button:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742479955714/306058d9-1629-4185-b480-d9e1ca4a6e91.jpeg" alt="upload page" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>On the Upload page, you will see the "Add files" and "Add folder" buttons. Let's start by uploading individual files. Click on "Add files" to begin selecting and uploading the necessary files.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742480188360/b4a5d67f-14ed-4675-b846-8e71cf9080d5.jpeg" alt="adding files" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>After successfully uploading your files, ensure you follow your project structure. If your static site only requires individual files, proceed accordingly. But if your project relies on specific folders for assets like images and CSS, you’ll need to upload those as well. In my case, my project structure includes folders for images and CSS e.t.c, so I will be uploading both files and folders.</p>
<h4 id="heading-3-upload-project-folders">3. Upload project folders</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742483020056/6454b351-b962-4e71-845b-b6c90a3bd2db.jpeg" alt="project folders" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>To upload your folders click on the “Add folder” button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742483175794/0f96e172-146a-4513-adda-4e7bfb5281ec.jpeg" alt="highlighing the Add folder button in the bucket page" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Now, upload your folders by clicking on the "Add folder" button and selecting the necessary folders, such as those containing images, CSS, or JavaScript files, based on your project structure.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742483490417/254d6fc4-ca3c-427d-acb5-1ffab46ed15f.jpeg" alt="how successful folder upload looks like " class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>You will notice that as you upload folders, S3 automatically extracts and structures the files by name, type, and size. This can sometimes be confusing for beginners, as the files are displayed in a structured format.</p>
<p>Once you have successfully uploaded your files and folders, scroll down and click the "Upload" button to complete the process.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742483726834/890c70db-e2e4-4b0f-aac3-29b215276f97.jpeg" alt="highlighting where the upload button is " class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>After clicking on "Upload", AWS S3 will begin uploading your files. You will see a progress indicator showing the status of each file being uploaded in real time.</p>
<p>Upon completing the upload, you will see a confirmation message stating "Upload Succeeded." AWS S3 will then generate a URL which you can find in the Summary section of the bucket’s details page. (Refer to the image below for reference.)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742485405264/af04f296-e447-4747-b060-2f9b86d7a0e0.jpeg" alt="showcasing Upload Succeeded " class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Congratulations! you have successfully uploaded your project into AWS S3. Now let’s make this accessible on the web.</p>
<p>Click on the "Close" button to exit the upload page. Now, while still in your bucket, let's configure the necessary settings and permissions to ensure proper access and functionality for our static website.</p>
<h2 id="heading-how-to-set-permissions-and-properties-in-aws-s3"><strong>How to Set Permissions and Properties in AWS S3</strong></h2>
<p>After uploading your files, the next step is to configure permissions and properties to ensure your static website functions correctly.</p>
<ul>
<li><p><strong>Permissions</strong>: By default, S3 objects are private. To make your website publicly accessible, you need to adjust the bucket policy and object permissions accordingly.</p>
</li>
<li><p><strong>Properties</strong>: You can configure various settings such as versioning, encryption, and static website hosting under the Properties tab of your bucket, but we won’t be going too deep as we are just going through the basics.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742485786885/3290bf47-6d32-4956-957c-527ba30357ed.jpeg" alt="bucket page with files" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Let’s start with setting up permissions:</p>
<h3 id="heading-permissions">Permissions</h3>
<p>By default, public access is blocked in S3 for security reasons. But for this tutorial, we will be enabling public access to ensure our static website is accessible to users.</p>
<p>In the image below, you can see that the public access is blocked by default:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742486056880/5e5ae64d-ee88-4f7e-af4e-989f40850d21.jpeg" alt="blocked public access" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>To enable public access, click on the "Edit" button in the top-right corner. Then, uncheck the "Block all public access" option. After that, click on "Save changes" to apply the update.</p>
<p>Now you can see that the public access has been blocked:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742486660352/50508b0d-0d51-4f48-8bf0-d2a6f01fc5c8.jpeg" alt="Blocked public access" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Public access is now enabled. Click on "Save changes" to confirm and apply the update.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742486981885/6a9d687f-8fef-40a8-b585-de9c23126aa2.jpeg" alt="Public access now enabled, click on save changes to validate the settings" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h4 id="heading-enabling-public-access">Enabling public access</h4>
<p>AWS S3 will prompt you to enter a confirmation text to validate the settings. Input the required text and confirm, and public access will be enabled successfully.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742487256155/d3efe5d2-e42f-46d6-8052-4d6cb9a99ce8.jpeg" alt="confirmation prompt" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>So now that we have enabled public access, let’s set a bucket policy.</p>
<h3 id="heading-what-is-a-bucket-policy"><strong>What is a Bucket Policy?</strong></h3>
<p>A bucket policy is a JSON-based access control policy that defines permissions for your S3 bucket. It allows you to specify who can access your bucket and what actions they can perform.</p>
<p>With a bucket policy, you can:</p>
<ul>
<li><p>Grant or restrict public access to objects in your bucket.</p>
</li>
<li><p>Allow specific AWS users or services to interact with your bucket.</p>
</li>
<li><p>Define read, write, or delete permissions for different users.</p>
</li>
</ul>
<p>In this next section, we will configure a bucket policy to make our static website publicly accessible.</p>
<h2 id="heading-how-to-set-up-a-bucket-policy"><strong>How to Set Up a Bucket Policy</strong></h2>
<p>Setting up a bucket policy is simple. Click on the "Edit" button in the Bucket Policy section and paste the following JSON policy into the editor:</p>
<pre><code class="lang-markdown">{
  "Version": "2012-10-17",
  "Statement": [
<span class="hljs-code">    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::your-bucket-name/*"
    }
  ]
}</span>
</code></pre>
<h4 id="heading-understanding-the-policy-attributes">Understanding the policy attributes:</h4>
<ul>
<li><p><strong>Version</strong>: Defines the policy language version. The <code>"2012-10-17"</code> version is the latest and most commonly used for S3 policies.</p>
</li>
<li><p><strong>Statement</strong>: A list of rules that define what actions are allowed or denied.</p>
</li>
<li><p><strong>Sid (Statement ID)</strong>: A unique identifier for the policy statement (optional but useful for reference).</p>
</li>
<li><p><strong>Effect:</strong> Specifies whether the rule allows or denies the specified action. In this case, it’s <code>"Allow"</code>.</p>
</li>
<li><p><strong>Principal</strong>: Defines who has access. The <code>"*"</code> means anyone (public access).</p>
</li>
<li><p><strong>Action</strong>: Specifies the allowed operation. <code>"s3:GetObject"</code> allows users to retrieve (read) objects from the bucket.</p>
</li>
<li><p><strong>Resource</strong>: Defines the specific bucket and objects the policy applies to. Replace <code>"your-bucket-name"</code> with your actual bucket name. The <code>"/*"</code> means all objects within the bucket.</p>
</li>
</ul>
<p>This policy makes all objects in the bucket publicly readable, allowing users to access your static website files via a browser.</p>
<p>if you followed closely, your bucket policy page should look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742489775324/07439366-6f64-401b-a48a-1ce20536df5c.jpeg" alt="07439366-6f64-401b-a48a-1ce20536df5c" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Now, click on the "Save changes" button, usually located at the bottom right of the page, to apply your changes.</p>
<p>After following the required steps Bucket policy has been set and public access has been enabled.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742490328543/58488d6e-1bc0-4acc-86da-e137e8fc5180.jpeg" alt=" Bucket policy has been set and public access has been enabled." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Congratulations! You have successfully set up your bucket policy and enabled public access, meaning anyone can now access your website. But wait, where's the URL?</p>
<p>There's one last step: we need to enable static website hosting. To do this, you’ll need to navigate to the Properties tab and configure static website hosting so your site can be accessed on the web. I’ll walk you through the process in the next section.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742490657866/e9bb25a9-7fc2-478b-a531-2da041d4b740.jpeg" alt="permissions tab" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-enable-static-hosting">How to Enable Static Hosting</h2>
<p>Enabling static website hosting is simple. Scroll to the bottom of the Properties tab, and you will find the Static website hosting section. By default, this option is disabled. Let's enable it to make our website accessible on the web.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742490851582/ffa1b0b1-82f4-40d4-951d-bc494681620e.jpeg" alt="static web hosting section in the permissions tab" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Click on the "Edit" button on the right side of the Static website hosting section. By default, this option is disabled, so change it to Enabled. Once enabled, you will see several configuration options, most of which are optional for this tutorial.</p>
<p>The most important setting here is the Index document, which specifies the default file that loads when someone accesses your site. The placeholder text indicates that it expects an <code>index.html</code> file. Simply type <code>index.html</code> in the Index document field to proceed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742491006060/91ba5d52-acb4-42c8-802d-7cfd9a24e8c8.jpeg" alt="setting up static hosting" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>After entering <code>index.html</code> in the Index document field, scroll down and click on "Save changes" to apply the configuration.</p>
<p>After successfully applying the changes, you should find your Static Website URL at the bottom of the Static website hosting section in the Properties tab of your bucket.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742491854680/8393bbf2-27e6-4d21-b41f-d48e8e03fb09.jpeg" alt="static website url " class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Congratulations! You have just hosted your website on AWS S3. That’s a solid step into the world of DevOps.</p>
<p>Your URL should look something like this: <a target="_blank" href="http://your-bucket-name.s3-website.your-region.amazonaws.com/"><code>http://your-bucket-name.s3-website.your-region.amazonaws.com/</code></a>.</p>
<p>Simply copy and paste your Static Website URL into your browser to see your hosted site live.</p>
<p>Now, you have the skills to host a static website for a client and share the URL with them.</p>
<p>So go grab some coffee and banana bread, you have earned it. Then we’ll move on to the next part of the tutorial.</p>
<h2 id="heading-amazon-cloudfront">Amazon CloudFront</h2>
<p>Amazon CloudFront is a fast, highly secure, and programmable content delivery network (CDN) that improves website performance and security.</p>
<p>Our static website is hosted on S3(<a target="_blank" href="http://your-bucket-name.s3-website.your-region.amazonaws.com/"><code>http://your-bucket-name.s3-website.your-region.amazonaws.com/</code></a>) – but you might notice that the site is accessible via HTTP but lacks proper security measures such as SSL/TLS encryption. CloudFront addresses these limitations by providing a secure and scalable way to serve both static and dynamic content globally.</p>
<p>It delivers content to users with low latency by caching copies of your website at edge locations worldwide. When a user requests a resource, CloudFront serves it from the nearest edge location, significantly improving speed and availability.</p>
<h3 id="heading-key-features-of-amazon-cloudfront">Key Features of Amazon CloudFront</h3>
<ol>
<li><p><strong>Secure content delivery</strong>: CloudFront supports SSL/TLS encryption, ensuring data is transferred securely between clients and servers.</p>
</li>
<li><p><strong>DDoS protection</strong>: Integrated with AWS Shield, CloudFront helps mitigate Distributed Denial of Service (DDoS) attacks.</p>
</li>
<li><p><strong>Global content caching</strong>: CloudFront caches content at multiple edge locations, reducing server load and latency.</p>
</li>
<li><p><strong>Customizable distribution</strong>: Users can configure cache behavior, origin settings, and security policies.</p>
</li>
<li><p><strong>Seamless integration with AWS tools</strong>: CloudFront integrates with AWS Lambda@Edge, S3, EC2, and API Gateway, supporting both static and dynamic content delivery.</p>
</li>
<li><p><strong>Cost optimization</strong>: Reduces data transfer costs by caching and serving content from edge locations instead of directly from the origin.</p>
</li>
</ol>
<h3 id="heading-why-amazon-s3-alone-is-not-enough">Why Amazon S3 Alone is Not Enough</h3>
<p>Amazon S3 is a scalable and durable object storage service, but it lacks key features required for securely serving web content.</p>
<p>First of all, it doesn’t have HTTPS by default. When hosting a site on S3, it is only accessible over HTTP unless additional configurations are made.</p>
<p>Second, it has higher latency. S3 buckets are hosted in a specific AWS region, which may result in slower content delivery for users in different locations.</p>
<p>It also doesn’t have built-in caching, which means that every request is served from S3, increasing response time and potential costs.</p>
<p>And finally, there’s no DDoS protection – unlike CloudFront, S3 does not provide native protection against cyberattacks.</p>
<h3 id="heading-why-you-should-serve-your-website-with-cloudfront">Why You Should Serve Your Website with CloudFront</h3>
<p>While Amazon S3 is a great storage solution, it lacks the security and performance optimizations needed for web hosting. Amazon CloudFront enhances security with SSL/TLS encryption, improves performance with global caching, and provides robust security features like DDoS protection.</p>
<p>By leveraging CloudFront, you can ensure your website is not only fast but also secure, scalable, and cost-effective.</p>
<p>To get started with hosting your site on CloudFront, navigate to the CloudFront service page in AWS. You can do this by going to your AWS Management Console homepage or dashboard and searching for “CloudFront” using the search bar in the top left corner. Click on “CloudFront” from the search results to proceed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742514549138/97c31675-81dc-4d6f-8f2c-28867bbb7ee2.jpeg" alt="cloud front can be navigated on the dashboard" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742514582663/14f0b8b4-3556-4ed2-a309-643d5fcee263.jpeg" alt="you can search for CloudFront on the search bar" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Once you navigate to the AWS CloudFront page, you will see a Create Distribution button. Click on it to begin setting up CloudFront for your website.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742515301740/ffe72934-b250-4adb-8cf7-e9cafd5c4ac2.jpeg" alt="ffe72934-b250-4adb-8cf7-e9cafd5c4ac2" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-what-is-a-cloudfront-distribution"><strong>What is a CloudFront Distribution?</strong></h3>
<p>A CloudFront distribution is the configuration that defines how CloudFront delivers content to users. It acts as a link between your origin server (such as an S3 bucket) and CloudFront’s global network of edge locations. When a user requests your site, CloudFront retrieves content from the nearest edge location instead of always fetching it from the origin, ensuring faster load times, reduced latency, and improved security.</p>
<p>There are two types of distributions in CloudFront:</p>
<ol>
<li><p><strong>Web Distribution</strong>: Used for websites, APIs, and dynamic or static content.</p>
</li>
<li><p><strong>RTMP Distribution</strong> (Deprecated): Previously used for streaming media (now replaced by modern streaming services).</p>
</li>
</ol>
<p>For our S3-hosted website, we will be creating a Web Distribution to securely serve content over HTTPS while improving speed and reliability.</p>
<p>After clicking the Create Distribution button, you will be directed to the Create Distribution page, where you can configure various settings. In this tutorial, we will focus on the essential options.</p>
<ol>
<li><p>Under the Origin section, select your S3 bucket as the origin domain.</p>
</li>
<li><p>If your S3 bucket has static website hosting enabled, AWS recommends using the S3 website endpoint instead of the default bucket endpoint.</p>
</li>
<li><p>Enter the correct S3 website endpoint in the Origin domain field. For example:</p>
<pre><code class="lang-markdown"> freecodecampbuckettutorial.s3-website.ca-central-1.amazonaws.com
</code></pre>
</li>
<li><p>Ensure you use the S3 website endpoint rather than the standard S3 bucket URL to avoid issues with accessing your site.</p>
</li>
</ol>
<p>By following these steps, you ensure that CloudFront correctly serves your static website from the S3 bucket.</p>
<p>Make sure that you use the S3 website endpoint rather than the standard S3 bucket URL to avoid issues with accessing your site. This will be suggested to you descriptively while filling in the origin name. Click on “Use website endpoint” and it will populate the field with the S3 bucket website endpoint instead of the bucket URL.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742516012771/9c631915-1a1e-415d-a614-5aab854db6ed.jpeg" alt="select distribution" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742515900001/01d173b4-8f2d-4289-96e1-e688ebafa34e.jpeg" alt="select s3 bucket on origin domain" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Next, scroll down to the Web Application Firewall (WAF) section at the bottom of the page and enable security protections to safeguard your website from common web threats. Select “Create Distribution” to deploy your CloudFront distribution.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742516241133/c587b52a-9734-40bb-80cc-a882b9bbbd44.jpeg" alt="enable waf" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Your CloudFront Distributions page should now display the newly created distribution. The page will include key details such as:</p>
<ul>
<li><p><strong>Distribution ID</strong>: A unique identifier for your CloudFront distribution.</p>
</li>
<li><p><strong>Domain Name</strong>: The CloudFront-provided URL (for example, <a target="_blank" href="http://d1234abcd.cloudfront.net"><code>d1234abcd.cloudfront.net</code></a>), which you can use to access your site.</p>
</li>
</ul>
<p>After creating your CloudFront distribution, navigate to your Distributions page and check the Last Modified section for its status. If the status shows Deploying, you will need to wait until it changes, which may take several minutes.</p>
<p>Once the deployment is complete, the status will typically update to a timestamp, indicating that the distribution is ready for use. Ensure the status has changed before proceeding with further configurations or accessing your CloudFront distribution.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742525346316/cd9d88a0-7c03-46d8-a794-f3ddd64ffdda.jpeg" alt="last modified page" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Your website is now successfully hosted on CloudFront!</p>
<p>We now have our CloudFront Domain Name (for example, <a target="_blank" href="http://d1234abcd.cloudfront.net"><code>d1234abcd.cloudfront.net</code></a>), which you can find in the Details section of your distribution. Before making any further changes, let’s preview the site by copying and pasting the domain name into a web browser.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742516439826/0bfd3393-8ec0-45cc-a643-0e1d31c013cf.jpeg" alt="content distribution created" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>At this stage, when you try to access your website using the CloudFront Domain Name, you’ll notice that the site cannot be reached. This happens because CloudFront does not yet have permission to fetch content from your S3 bucket.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742518594690/0808d92b-b8c5-45b3-a89c-f54452eec9a6.jpeg" alt="cloud front domain unreachable page" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>To fix this, you need to update your S3 bucket policy to explicitly allow CloudFront to access your objects. You can do this by adding a condition that grants access to requests coming specifically from your CloudFront distribution.</p>
<h3 id="heading-understanding-the-amazon-resource-name-arn"><strong>Understanding the Amazon Resource Name (ARN)</strong></h3>
<p>An Amazon Resource Name (ARN) is a unique identifier assigned to AWS resources. Every CloudFront distribution has its own ARN, which you can find at the top of the CloudFront distribution details page. It looks like something like this:</p>
<pre><code class="lang-markdown">arn:aws:cloudfront::123456789012:distribution/E2ABC3XYZ456
</code></pre>
<p>This ARN is crucial because we use it in our bucket policy to restrict access to only our CloudFront distribution, ensuring no other services or users can retrieve data directly from our S3 bucket.</p>
<h3 id="heading-updated-s3-bucket-policy"><strong>Updated S3 Bucket Policy</strong></h3>
<p>To allow CloudFront to serve content from our S3 bucket, we update the S3 bucket policy as follows:</p>
<pre><code class="lang-markdown">{
  "Version": "2012-10-17",
  "Statement": [
<span class="hljs-code">    {
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudfront.amazonaws.com"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::your-bucket-name/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::[ACCOUNT_ID]:distribution/[DISTRIBUTION_ID]"
        }
      }
    },
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::your-bucket-name/*"
    }
  ]
}</span>
</code></pre>
<p>Breaking down the bucket policy:</p>
<ol>
<li><p><strong>First statement that grants CloudFront access:</strong></p>
<ul>
<li><p><code>“Effect”: “Allow”</code>: This permits access to the specified resource.</p>
</li>
<li><p><code>"Principal": { "Service": "</code><a target="_blank" href="http://cloudfront.amazonaws.com"><code>cloudfront.amazonaws.com</code></a><code>" }</code>: Grants access specifically to CloudFront.</p>
</li>
<li><p><code>"Action": "s3:GetObject"</code>: Allows CloudFront to retrieve objects from the S3 bucket.</p>
</li>
<li><p><code>"Resource": "arn:aws:s3:::your-bucket-name/*"</code>: Grants access to all objects in the S3 bucket.</p>
</li>
<li><p><code>“Condition”</code>: Ensures that only requests originating from our CloudFront distribution are allowed using:</p>
<pre><code class="lang-markdown">  AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/E2ABC3XYZ456
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Second statement (optional, and grants public access)</strong></p>
<ul>
<li><p>This statement allows all users (<code>Principal: "*"</code>) to access the S3 objects.</p>
</li>
<li><p>If you want to restrict access only to CloudFront, you can remove this second statement.</p>
</li>
</ul>
</li>
</ol>
<p>After editing and updating your S3 bucket policy to allow CloudFront access, you can refresh the page where you preview your CloudFront domain name (for example, <a target="_blank" href="http://d1234abcd.cloudfront.net"><code>d1234abcd.cloudfront.net</code></a>). Open it in a browser, and if you have carefully followed all the instructions, you have successfully hosted a static site on S3 and CloudFront.</p>
<p>Your website is now fully protected, well done! Congratulations, DevOps pro!</p>
<p><strong>FIST BUMPS!</strong></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this guide, we’ve only scratched the surface of what AWS can do. S3 and CloudFront are powerful services, but there’s still so much more you can explore, from advanced security settings to automation and performance optimizations.</p>
<p>As you continue your AWS journey, you can dive deeper into topics like caching strategies, custom domain configurations, and integrating AWS Lambda@Edge for dynamic content. The possibilities are endless.</p>
<p>This is just the beginning, and you’re off to a great start. Keep experimenting, keep learning, and soon we will be mastering even more advanced AWS capabilities. Happy building! 🚀</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to host and deploy a static website or JAMstack app to AWS S3 and CloudFront ]]>
                </title>
                <description>
                    <![CDATA[ S3 and CloudFront are AWS cloud services that make serving static assets powerful and cheap. How can we host a simple static website or JAMstack app on it? A little about AWS What are the benefits of serving from S3 and CloudFront? Before we start, ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-host-and-deploy-a-static-website-or-jamstack-app-to-s3-and-cloudfront/</link>
                <guid isPermaLink="false">66bee8fdf53892da32acd273</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ beginners guide ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud Services ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud Solutions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cloudfront ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JAMstack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ S3 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Static Site Generators ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Wed, 11 Mar 2020 13:16:31 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/03/static-website-in-aws-s3.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>S3 and CloudFront are AWS cloud services that make serving static assets powerful and cheap. How can we host a simple static website or JAMstack app on it?</p>
<ul>
<li><a class="post-section-overview" href="#heading-a-little-about-aws">A little about AWS</a></li>
<li><a class="post-section-overview" href="#heading-what-are-the-benefits-of-serving-from-s3-and-cloudfront">What are the benefits of serving from S3 and CloudFront?</a></li>
<li><a class="post-section-overview" href="#heading-before-we-start-youll-need-an-aws-account">Before we start, you’ll need an AWS account</a></li>
<li><a class="post-section-overview" href="#heading-storing-your-website-on-s3">Storing your website on S3</a></li>
<li><a class="post-section-overview" href="#heading-serving-your-website-on-s3">Serving your website on S3</a></li>
<li><a class="post-section-overview" href="#heading-distributing-your-website-on-cloudfront">Distributing your website on CloudFront</a></li>
<li><a class="post-section-overview" href="#heading-custom-domain-names">Custom domain names</a></li>
<li><a class="post-section-overview" href="#heading-advanced-aws-usage">Advanced AWS Usage</a></li>
<li><a class="post-section-overview" href="#heading-resources">Resources</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/1lDGDzmbQWg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-a-little-about-aws">A little about AWS</h2>
<p>If you’re not familiar, <a target="_blank" href="https://aws.amazon.com/">AWS</a> (Amazon Web Services) is a cloud service provider that gives developers opportunities to build pretty much anything they can imagine in the cloud.</p>
<p>Though their <a target="_blank" href="https://aws.amazon.com/products/">services</a> extend beyond the likes of <a target="_blank" href="https://aws.amazon.com/machine-learning/">machine learning</a> and <a target="_blank" href="https://aws.amazon.com/ai/">artificial intelligence</a>, we’re going to stick with the entry level services for the purpose of this guide that will allow us to easily host an HTML website.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-services-overview.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Types of AWS services available</em></p>
<p>Building a site with S3 and CloudFront is a common recipe that small and high scale companies across the web use, but let’s break down what each service actually does.</p>
<h3 id="heading-object-storage-with-s3">Object storage with S3</h3>
<p><a target="_blank" href="https://aws.amazon.com/s3/">S3</a> (Simple Storage Service) acts as your hosting for your static website. Think of it like a hard drive in the cloud which we’re not able to use it for processing purposes, but rather for simple file storage and access.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket-file-list.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>List of files from a static site in an AWS S3 bucket</em></p>
<p>When an app or website is compiled in static form, this is all we need to serve it to the people visiting our site. The HTML is sent in the initial request “as is” (unless there’s processing with your provider) and any additional work occurs after the page loads in the browser usually by JavaScript. This allows us to take this simple (and cheap) approach by serving these files from S3.</p>
<h3 id="heading-content-delivery-network-with-cloudfront">Content Delivery Network with CloudFront</h3>
<p><a target="_blank" href="https://aws.amazon.com/cloudfront/">CloudFront</a> works as a <a target="_blank" href="https://en.wikipedia.org/wiki/Content_delivery_network">CDN</a> (Content Delivery Network) that sits in front of your website, caching the files, and serving them directly to the people visiting your site.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/cdn-distribution-map.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>CDN Diagram</em></p>
<p>Where you host and serve your website from, typically called the origin, is the main source of your files and can serve the website itself. But putting a CDN in front of it provides the people accessing your content a shorter and faster way to make their request.</p>
<h2 id="heading-what-are-the-benefits-of-serving-from-s3-and-cloudfront">What are the benefits of serving from S3 and CloudFront?</h2>
<p>Given the rise in the <a target="_blank" href="https://jamstack.org/">JAMstack</a> era, many services are popping up that provide similar services for static sites that make it really easy to deploy. Some even come with a generous free tier like <a target="_blank" href="https://www.netlify.com/">Netlify</a> and <a target="_blank" href="https://zeit.co/">Zeit</a>!</p>
<p>But sometimes developers need a little bit more control over their services or they need to integrate into a larger cloud pipeline that’s already 99% percent in AWS, which is exactly where S3 shines. Also, chances are, during your first year you might still qualify for AWS’s <a target="_blank" href="https://aws.amazon.com/free/">free tier</a>.</p>
<h3 id="heading-fitting-in-to-the-aws-well-architected-framework">Fitting in to the AWS Well-Architected Framework</h3>
<p>As a lead provider in cloud services, AWS has published many guides to help developers and teams strive for excellence in their solutions in terms of performance, cost, and security.</p>
<p>One particular guideline is their 5 pillars of what they describe as a <a target="_blank" href="https://aws.amazon.com/architecture/well-architected/">“well-architected" infrastructure</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-well-architected-framework.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>AWS Well-Architected Framework</em></p>
<p>By default, we check all of these boxes with our hosting solution by using S3 and CloudFront. Out of the box, the HTML and assets you serve will be fast, cheap, secure, and reliable.</p>
<h3 id="heading-the-beauty-of-static-and-jamstack-sites">The beauty of static and JAMstack sites</h3>
<p>Building on top of the pillars, what you’re actually serving is a static HTML file and group of assets that won’t require any type of rendering resources on the initial request. Before this, a common problem was having to worry about a site crashing due to heavy load. But with S3 and CloudFront, your website is infinitely scalable.</p>
<p>On a similar note, when that server scales up as it's trying to serve millions of hits on your post that went viral, so will your costs. Serving a static site is cheap and can greatly reduce the cost associated with running a web server.</p>
<h2 id="heading-before-we-start-youll-need-an-aws-account">Before we start, you’ll need an AWS account</h2>
<p>To work through this guide, you’ll need an AWS account. Luckily, it's free to create an account – you’ll only pay for the services used.</p>
<p>On top of that, AWS provides a generous free tier for some of its services. Some services provide only 12 months of a free tier (like S3) where others are always eligible for the free tier (like <a target="_blank" href="https://aws.amazon.com/lambda/">Lambda</a>), so make sure to do your homework so you don’t rack up an unexpectedly high bill.</p>
<p>To create your account, head over to the AWS website and then continue on to get started: <a target="_blank" href="https://aws.amazon.com/">https://aws.amazon.com/</a>.</p>
<h2 id="heading-storing-your-website-on-s3">Storing your website on S3</h2>
<p>To get started, we’re going to begin with a simple HTML file that will serve as our website. This will allow us to focus more on the process of hosting rather than the intricacies of the website itself.</p>
<h3 id="heading-creating-our-website-file">Creating our website file</h3>
<p>Begin by creating a new folder called <code>my-static-site</code>. Inside that folder, let's create a new file called <code>index.html</code> and add the following to the file:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">“en”</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">“UTF-8”</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">“viewport”</span> <span class="hljs-attr">content</span>=<span class="hljs-string">“width</span>=<span class="hljs-string">device-width,</span> <span class="hljs-attr">initial-scale</span>=<span class="hljs-string">1.0”</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>My Static Website<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hello World!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This is my static website. ?<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>If you open this file from your computer in your favorite browser, you should now be seeing this.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/hello-world-local-website-file-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Hello World! Opening a local webpage</em></p>
<h3 id="heading-creating-a-new-bucket">Creating a new bucket</h3>
<p>Head on over to your AWS account, log in, and navigate to your <a target="_blank" href="https://s3.console.aws.amazon.com/s3/">S3 console</a>.</p>
<p>Once there, let’s create our bucket by clicking on the blue <strong>Create bucket</strong> button:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-create-bucket.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a bucket in AWS S3</em></p>
<p>The first thing AWS wants us to do is enter a <strong>Bucket name</strong>. The bucket name must be globally unique, meaning, the name you use can be the only one in the world, so let’s try something like <code>[yourname]-static-website</code>, where I’ll use <code>colbyfayock-static-website</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket-name.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Naming a bucket in AWS S3</em></p>
<p>Next, let’s set the <a target="_blank" href="https://aws.amazon.com/about-aws/global-infrastructure/regions_az/"><strong>Region</strong></a>. This is the geographic location where AWS will host the bucket and your website. You’re probably fine with the default, but if you’d like, you can select the location closest to you if it’s permitted. Since I’m in Virginia, I’m going to stick with my default of <strong>US East (N. Virginia)</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket-region.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Setting the region of a bucket in AWS S3</em></p>
<p>Finally, hit the <strong>Create</strong> button on the bottom left of the page.</p>
<p><em>Note: even if you use the <code>[yourname]-static-website</code> pattern, there’s a chance the name will be taken. If it’s taken, AWS will show an error stating “Bucket name already exists,” at which point you’ll want to try a new name of your choosing.</em></p>
<p>Alternatively, you can hit <strong>Next</strong> for advanced usage, but for this guide, we’re okay with all of the defaults S3 provides.</p>
<p>If successful, you should now see your bucket in the list on the S3 console dashboard.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>New bucket in AWS S3</em></p>
<h3 id="heading-uploading-your-website-to-the-bucket">Uploading your website to the bucket</h3>
<p>Let’s navigate to our new bucket by clicking the row of our bucket. You’ll be greeted with a message stating “This bucket is empty. Upload new objects to get started,” so that’s what we’ll do.</p>
<p>Click the <strong>Upload</strong> button to get started.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket-upload.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Uploading files to AWS S3</em></p>
<p>You’ll then see a popup that will ask you to upload a file. Click on the <strong>Add files</strong> button and select your <code>index.html</code> file we created earlier.</p>
<p>Once selected, click the <strong>Upload</strong> button on the bottom left.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket-upload-files.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Selecting files to upload in AWS S3</em></p>
<p>And now your file is uploaded to S3!</p>
<h2 id="heading-serving-your-website-on-s3">Serving your website on S3</h2>
<p>If you try to navigate to your <code>index.html</code> file and open it, you’ll notice a big ugly "Access Denied" message.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-access-denied.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Access Denied to bucket file</em></p>
<p>This is because your file doesn’t currently have the permissions and settings necessary to serve the file to the public, so let’s fix that.</p>
<h3 id="heading-setting-up-your-bucket-as-a-website">Setting up your bucket as a website</h3>
<p>Navigate to the <strong>Properties</strong> tab inside of your bucket, then click <strong>Static website hosting</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-properties-static-hosting.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Setting up an AWS S3 bucket for statice website hosting</em></p>
<p>Once there, we want to do a few things:</p>
<ul>
<li>Note down the <strong>Endpoint</strong> at the top of the block. We’ll use this to access our site later (you can always find this here again)</li>
<li>Select the “Use this bucket to host a website” option</li>
<li>Enter <code>index.html</code> in the <strong>Index document</strong> field</li>
<li>Finally hit <strong>Save</strong></li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-static-website-configuration.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Configuring an AWS S3 bucket for static website hosting</em></p>
<h3 id="heading-setting-up-your-bucket-policy-and-permissions">Setting up your bucket policy and permissions</h3>
<p>Next, navigate to the <strong>Permissions</strong> tab. Here we’ll want to do 2 things: unblock all public access and add a Bucket Policy.</p>
<p>First, on the main page, let’s click <strong>Edit</strong> to unblock all access.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket-permissions.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Configuring an AWS S3 bucket permissions</em></p>
<p>Then, uncheck the “Block all public access” checkbox and hit <strong>Save</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-bucket-block-access.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Allowing public access to an AWS S3 bucket</em></p>
<p>AWS will ask you to confirm these settings, as this may not always be what you want to do with your bucket. But for the purposes of hosting a website, we want the whole world to see, so type in the word “confirm” and hit the <strong>Confirm</strong> button.</p>
<p>After confirming, click the <strong>Bucket policy</strong> button and you’ll be taken to a text editor.</p>
<p>In this text box, we’ll want to paste the following snippet. Within this snippet, make sure to replace <code>[your-bucket-name]</code> with the name of your bucket, otherwise you will not be able to save this file.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"Version"</span>:<span class="hljs-string">"2012-10-17"</span>,
  <span class="hljs-attr">"Statement"</span>:[{
    <span class="hljs-attr">"Sid"</span>:<span class="hljs-string">"PublicReadGetObject"</span>,
        <span class="hljs-attr">"Effect"</span>:<span class="hljs-string">"Allow"</span>,
      <span class="hljs-attr">"Principal"</span>: <span class="hljs-string">"*"</span>,
      <span class="hljs-attr">"Action"</span>:[<span class="hljs-string">"s3:GetObject"</span>],
      <span class="hljs-attr">"Resource"</span>:[<span class="hljs-string">"arn:aws:s3:::[your-bucket-name]/*”
      ]
    }
  ]
}</span>
</code></pre>
<p><a target="_blank" href="https://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteAccessPermissionsReqd.html#bucket-policy-static-site">This policy</a> states that it’s allowing the public to perform a GetObject request on the S3 resource, which is your S3 bucket.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-static-website-bucket-policy.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Setting up a public policy for an AWS S3 bucket</em></p>
<p>After you add the policy, click the <strong>Save</strong> button. Your should now see a message stating "This bucket has public access.”</p>
<h3 id="heading-previewing-your-new-bucket-website">Previewing your new bucket website</h3>
<p>If you noted down the Endpoint from your Properties page, you can now visit that address to see your website. The endpoint should look like this:</p>
<pre><code class="lang-plaintext">http://[your-bucket-name].s3-website-[region-id].amazonaws.com
</code></pre>
<p>If you didn’t, jump back up a few steps to remind yourself how to find it or look under the Properties tab.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-s3-static-website.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Hello World! Opening an AWS S3 website</em></p>
<p>Congrats, you're halfway there! ?</p>
<h2 id="heading-distributing-your-website-on-cloudfront">Distributing your website on CloudFront</h2>
<p>Now that we have our static website being served from a bucket on S3, let’s take it up another level and serve it across the world using CloudFront.</p>
<h3 id="heading-creating-a-cloudfront-distribution">Creating a CloudFront distribution</h3>
<p>Navigate to your <a target="_blank" href="https://console.aws.amazon.com/cloudfront">CloudFront dashboard</a> and click the <strong>Create Distribution</strong> button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-create-distribution.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a new distribution in AWS CloudFront</em></p>
<p>Next, select <strong>Get Started</strong> under the <strong>Web</strong> delivery method.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-creating-web-distribution.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Getting started with an AWS CloudFront distribution with Web delivery</em></p>
<p>Here, we’ll enter a few custom parameters to get our distribution set up.</p>
<p>Click into the <strong>Origin Domain Name</strong> field. Once selected, a dropdown list should appear where you can select the S3 bucket you just created. Go ahead and select your S3 bucket.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-distribution-origin-name-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Setting the origin domain name in AWS CloudFront to your bucket</em></p>
<p>While you can <a target="_blank" href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html">customize most of the settings</a> to your liking, for our purposes, we’re going to leave all as their default values except for one.</p>
<p>Scroll down to the <strong>Default Root Object</strong> field and type <code>index.html</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-distribution-default-root-object-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Setting the Default Root Object for a distribution in AWS CloudFront</em></p>
<p>After, scroll down to the bottom and click <strong>Create Distribution</strong> in the bottom right.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-setup-create-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating an AWS CloudFront distribution</em></p>
<h3 id="heading-previewing-your-new-cloudfront-distribution">Previewing your new CloudFront distribution</h3>
<p>After hitting the <strong>Create</strong> button, it will take some time for your distribution to be created and set up. You’ll notice on the <strong>CloudFront Distributions</strong> list page that the <strong>Status</strong> of your new distribution is <strong>In Progress</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-distribution-in-progress-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>AWS CloudFront distribution deployment is In Progress</em></p>
<p>Once this completes, it will say <strong>Deployed</strong>. Then you can find your <strong>Domain Name</strong> in the same row.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-distribution-deployed.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>AWS CloudFront distribution is Deployed</em></p>
<p>Using the value in the Domain Name column, open your distribution in your browser and success! You now are viewing your S3 bucket through CloudFront’s distribution network.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudfront-static-website-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Hello World! Opening an AWS CloudFront website</em></p>
<h2 id="heading-custom-domain-names">Custom domain names</h2>
<p>While most of us will probably want to use a custom domain name with our website, we’re not going to dive too deep into that this guide, as there are many ways to set that up depending on where you purchase your domain name.</p>
<p>However, here are a few things to consider.</p>
<h3 id="heading-https-ssl-certificate">HTTPS / SSL Certificate</h3>
<p>If you’re creating your CloudFront distribution to use with a custom domain name, you'll most likely want to configure your distribution with an <a target="_blank" href="https://www.cloudflare.com/learning/ssl/what-is-an-ssl-certificate/">SSL certificate</a> using AWS’s <a target="_blank" href="https://aws.amazon.com/certificate-manager/">Certificate Manager</a>. Alternatively you can provide your own certificate with tools like <a target="_blank" href="https://letsencrypt.org/">Let's Encrypt</a>, but by using ACM, AWS makes it easy to pull in the records for use with your distribution.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/fay.io-ssl-certificate.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Once in ACM, you’ll want to configure the certificate, map what domains and subdomains should match (typically <code>*.domain.com</code>), and then create your certificate to use with your distribution.</p>
<p>To get started, you can check out the AWS guide for <a target="_blank" href="https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html">requesting a public certificate</a>.</p>
<h3 id="heading-cnames-and-aliases">CNAMEs and Aliases</h3>
<p>A common approach to setting up a custom domain is to use a CNAME. CloudFront makes this pretty painless, as you’ll add it as a configuration option when you’re configuring your distribution.</p>
<p>To get started with setting up a CNAME in CloudFront, <a target="_blank" href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/CNAMEs.html">see the AWS guide</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/fay.io-route53-alias.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you’re using <a target="_blank" href="https://aws.amazon.com/route53/">Route53</a> to manage your <a target="_blank" href="https://www.cloudflare.com/learning/dns/what-is-dns/">DNS</a>, you can then set up an A record (alias) to point to your distribution. You can learn more <a target="_blank" href="https://aws.amazon.com/premiumsupport/knowledge-center/route-53-create-alias-records/">using this guide</a>.</p>
<h2 id="heading-advanced-aws-usage">Advanced AWS Usage</h2>
<p>For this guide, we walked you through setting up a new static website and app using the AWS console. But whether you want to learn more, improve your deploy efficiency, or want to automate this process, you’ll want to take a it a step further with the AWS CLI or CloudFormation.</p>
<p>While we won’t walk you through how to use these tools here, we’ll get you started with a little bit of an idea of what you’re up against.</p>
<h3 id="heading-aws-cli">AWS CLI</h3>
<p>The <a target="_blank" href="https://aws.amazon.com/cli/">AWS CLI</a> allows someone to perform AWS operations from the command line. This can be incredibly powerful when you want to script out your resource creation or if you simply prefer to do all of your work from the terminal.</p>
<p>Once set up locally, you’ll be able to perform actions like creating a bucket using the following command:</p>
<pre><code class="lang-shell">aws s3api create-bucket —-bucket [your-bucket-name] —-region [bucket-region]
</code></pre>
<p>To get started, check out the AWS CLI <a target="_blank" href="https://github.com/aws/aws-cli">Github page</a> or the AWS CLI <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html">User Guide</a>    .</p>
<h3 id="heading-aws-cloudformation">AWS CloudFormation</h3>
<p>AWS preaches “infrastructure as code.” It’s the idea that you can spin up your infrastructure using something that’s written in a file, where in this particular case, it would be a CloudFormation template. This allows you to have a repeatable process that will be the same each time you perform the deploy.</p>
<p><a target="_blank" href="https://aws.amazon.com/cloudformation/">CloudFormation</a> allows you to set up a configuration file that will deploy the services and resources of your choosing by pointing to that file with the CLI or by uploading it in the console.</p>
<p>Here’s an <a target="_blank" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/quickref-s3.html#scenario-s3-bucket-website">example from AWS</a> of what that looks like for a static S3 bucket that could serve as a website.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/aws-cloudformation-template-s3.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>AWS CloudFront template example</em></p>
<p>To get started, check out AWS’s CloudFormation <a target="_blank" href="https://aws.amazon.com/cloudformation/resources/templates/">example templates</a> or their <a target="_blank" href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/GettingStarted.Walkthrough.html">Get Started guide</a>.</p>
<h2 id="heading-resources">Resources</h2>
<p>If you’re interested in getting deeper into the AWS ecosystem, here are a few resources to get started:</p>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/aws-certified-cloud-practitioner-training-2019-free-video-course/">AWS Certified Cloud Practitioner Training 2019 - A Free 4-hour Video Course</a> (freeCodeCamp.org)</li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/awscertified-challenge-free-path-aws-cloud-certifications/">Introducing The #AWSCertified Challenge: A Path to Your First AWS Certifications</a> (freeCodeCamp.org)</li>
<li><a target="_blank" href="https://aws.amazon.com/getting-started/tutorials/">10-Minute Tutorials</a> (AWS)</li>
<li><a target="_blank" href="https://acloud.guru/">A Cloud Guru</a> (Paid courses)</li>
<li><a target="_blank" href="https://aws.amazon.com/solutions/case-studies/">AWS Case Studies</a> (AWS)</li>
</ul>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="2000" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Host a Static Website with S3, CloudFront and Route53 ]]>
                </title>
                <description>
                    <![CDATA[ By Paul Berg I recently set-up my self-hosted personal blog and I underestimated the effort I had to put in to make it exactly as I wanted. So I decided to write a tutorial to help others do it with less overhead. This article will go into fine detai... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-host-a-static-website-with-s3-cloudfront-and-route53-7cbb11d4aeea/</link>
                <guid isPermaLink="false">66c352a139769b84d9fe9730</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cloudfront ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 08 Jan 2019 16:17:52 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*mY5cuvV50_Jpya9yTWpzdQ.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Paul Berg</p>
<p>I recently set-up my self-hosted personal <a target="_blank" href="https://paulrberg.com/">blog</a> and I underestimated the effort I had to put in to make it exactly as I wanted. So I decided to write a tutorial to help others do it with less overhead.</p>
<p>This article will go into fine details on how to tick all the boxes below, with a focus on the backend components.</p>
<ol>
<li>Pay-as-you-go hosting</li>
<li>SSL certificate</li>
<li>Functional www subdomain</li>
<li>Highly customisable but minimalistic design</li>
<li>Markdown-powered articles</li>
</ol>
<p>For 4 and 5 above, I used <a target="_blank" href="https://gohugo.io/">Hugo</a> with the <a target="_blank" href="https://themes.gohugo.io/minimal/">Minimal</a> theme.</p>
<h4 id="heading-caveat">Caveat</h4>
<p>Do note that this is a verbose tutorial, aimed at those who value flexibility and interoperability with other AWS services more than anything else. If you’re just looking for something light and quick, you may want to use <a target="_blank" href="https://netlify.com/">Netlify</a> or <a target="_blank" href="https://aws-amplify.github.io/">Amplify</a>.</p>
<h4 id="heading-prerequisites">Prerequisites</h4>
<p>I will further assume that:</p>
<ol>
<li>You designed and coded your website or at least have a mockup.</li>
<li>You have an AWS account (if not, go <a target="_blank" href="https://portal.aws.amazon.com/billing/signup">register one</a>. AWS Accounts include one year of free tier access).</li>
<li>You are familiar with DNS and <a target="_blank" href="https://www.cloudflare.com/learning/dns/what-is-dns/">how it works</a>, at least at a high level.</li>
</ol>
<p>Regarding DNS, a quick explanation is that it’s sort of the directory of the Internet and, just like Google owns <code>google.com</code>, you can own your own domain such as <code>example.com</code> as well. To do it, you have to go a DNS registrar and purchase your the domain you want. I strongly recommend using <a target="_blank" href="https://namecheap.pxf.io/c/1243704/386170/5618">Namecheap</a> as your registrar, as they have an awesome UI and low prices. As an alternative, you could choose <a target="_blank" href="https://godaddy.com">GoDaddy</a>.</p>
<p>In case your “.com” domain is taken and you want some clever mashups, the following sites would be helpful:</p>
<ul>
<li><a target="_blank" href="https://leandomainsearch.com/">LeanDomainSearch</a></li>
<li><a target="_blank" href="https://wordoid.com/">Wordoid</a></li>
<li><a target="_blank" href="https://domainr.com/">Domainr</a></li>
</ul>
<p>After you purchase it, don’t set any DNS records yet. We’ll do that later once we get to Route53.</p>
<h4 id="heading-hosting-with-amazon-aws">Hosting with Amazon AWS</h4>
<p>As mentioned above, the goal is to use a pay-as-you-go service because it’s by far the most cost-effective option out there. I used to pay a fixed cost in the range of tens of USD per month for a server even if I had periods when there was hardly any activity on it.</p>
<p>However, from my experience, I would recommend that you go modular and go with a pay-as-you-go service such as AWS.</p>
<p>Before jumping in, it’s important to grasp the nomenclature:</p>
<ul>
<li>AWS: Amazon Web Services</li>
<li>S3: Simple Storage Service, for storing files</li>
<li>Route53: A service for handling DNS records</li>
<li>CloudFront: content delivery network (CDN) for speeding up your website, also required to generate the SSL certificate</li>
</ul>
<p>Here’s a neat <a target="_blank" href="https://cloudcraft.co/view/d2391653-9c67-4bcd-84f2-977b0e32ecfc?key=aoBnq-ksfVXmgA4yjWIWSQ">Mindmap</a> designed with <a target="_blank" href="https://cloudcraft.co/">Cloudcraft</a> for what you’re going to build:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/RCkmAwdM0GuBLFaTJiqr69WGHCi05pJHZb1y" alt="Image" width="800" height="450" loading="lazy"></p>
<p>We’ll first focus on the path on the <strong>right-hand side</strong>, so the normal configuration (with Route53, CloudFront and S3), not the one for the www subdomain. Importantly, using this modular configuration, you won’t run any backend Linux server at all, so you don’t have to worry about updating or patching anything. How convenient is that?</p>
<h4 id="heading-amazon-simple-stoarage-service-s3">Amazon Simple Stoarage Service (S3)</h4>
<p>This is where you’ll store your static files (HTML, CSS, JavaScript). If you used Create React App or some other frontend development framework, look for your generated “build” or “public” folder.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/fnj6dQpZoJ7f5DJ1zIFpJW1lz2o6Uq4uz2sq" alt="Image" width="320" height="387" loading="lazy"></p>
<p>Here’s what you have to do:</p>
<ol>
<li>Set up an S3 bucket named “example.com”. Notice that S3 bucket names are <a target="_blank" href="https://stackoverflow.com/questions/24112647/why-are-s3-and-google-storage-bucket-names-a-global-namespace">global</a> and, just like with domains, you’ll have to find another name if someone has taken <code>example.com</code> before you. Based on your needs, you can enable or disable the options AWS provides you: versioning, server access logging, encryption, etc.</li>
<li>Make sure to uncheck the boxes that mention blocking and removing public access ACLs and policies. Many times, S3 buckets are used to store private data, so AWS optimises the configuration for highly secure configurations. In your case though, you want to have the bucket publicly accessible.</li>
<li>Make sure to set a policy, here’s an <a target="_blank" href="https://gist.github.com/PaulRBerg/61e0c998f105fedb627fa66ff2c6aea6">example</a>.</li>
<li>Activate “Static website hosting” for your bucket and check “Use this bucket to host a website”</li>
<li>Upload your files, making sure “index.html” is at the root of your bucket</li>
</ol>
<p>All the operations above can be done using either the <a target="_blank" href="http://console.aws.amazon.com/">AWS Management Web Console</a> or the <a target="_blank" href="https://github.com/aws/aws-cli">AWS CLI</a>. Specifically for step 4 though, I’d recommend doing it in the console so that you can get the endpoint for your new hosted website (I hid mine for privacy reasons):</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/WKY2huZtZz18qLUplSfDof0L9d5VdcnZSi7N" alt="Image" width="750" height="893" loading="lazy"></p>
<p>Test it out in the browser to make sure you set up your S3 bucket correctly. It should like this:</p>
<blockquote>
<p>example.com.s3-website.your-region.amazonaws.com</p>
</blockquote>
<h4 id="heading-cloudfront">CloudFront</h4>
<p>To host a static website, you don’t actually need CloudFront or any other CDN, because there’s not much data to store and the gains in efficiency and UX are little. However, one of the original goals was to have a website secured by an SSL certificate so we’ll be using CloudFront.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/3rjcxs8vIoL988yxADq-guuSj6cXvaEWY2Tb" alt="Image" width="450" height="450" loading="lazy"></p>
<p>Now, you might’ve heard about CloudFlare, which is arguably the easiest way to get up and running with a CDN and it also provides the benefit of some SSL security. I say “some” because they have this misleading feature called “Flexible SSL”, which <a target="_blank" href="http://disq.us/p/1ycwtny">doesn’t have the security guarantees</a> a self-signed SSL certificate has.</p>
<p>Therefore, you’re not going to use that, but instead make use of a similar service in AWS called CloudFront. You can think of it as having your own content distribution servers, as data is cached in multiple locations around the world to provide your users fast response times. More important for the static website, it also makes using an SSL certificate possible.</p>
<p>Again, you can create your CloudFront distribution using the AWS administrator interface or the CLI tool. Here’s an example <a target="_blank" href="https://gist.github.com/PaulRBerg/7d946e54c8f5cfc22f514855c6b6e864">configuration</a>.</p>
<p>Caveats:</p>
<ol>
<li>The origin name should be the endpoint you got after activating “Static website hosting” on your S3 bucket.</li>
<li>Do NOT set any “DefaultRootObject”. Leave it empty.</li>
<li>Allow both HTTP and HTTPS. You’ll be able to automatically redirect users from HTTP to HTTPS after the certificate is signed and installed.</li>
</ol>
<p>Make sure to wait a while for the distribution to properly boot (can take up to 15 minutes). Test it by opening the endpoint you receive, your S3 static website should pop up. The endpoint should look like this:</p>
<blockquote>
<p><em>13fb4knzujxq0b.cloudfront.net</em></p>
</blockquote>
<p>Note down your CloudFront endpoint somewhere because we’ll use it with Route53 in a sec.</p>
<h3 id="heading-route53">Route53</h3>
<p>It’s time to connect the domain you bought on your DNS registrar with CloudFront and S3. Route53 acts as the bridge for that.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/9bjF7WQmstIAdJvspRcxpibs7CscN0fMRuZm" alt="Image" width="301" height="301" loading="lazy"></p>
<p>Here is what you’ll have to do configure Route53 and connect the domain with CloudFront:</p>
<ol>
<li>Create a Route53 hosted zone and set your domain. Make it public.</li>
<li>You’ll be given 4 NS records. Copy and paste the nameservers in your external domain administration page. If you’re using <a target="_blank" href="https://namecheap.pxf.io/c/1243704/386170/5618">Namecheap</a>, here’s how you can update your nameservers. Within Namecheap, Go to Account -&gt; Dashboard -&gt; Manage -&gt; Nameservers -&gt; Custom DNS and put your 4 nameservers in there:</li>
</ol>
<p><img src="https://cdn-media-1.freecodecamp.org/images/YVjqg3qgpaZP6JCDi2v3c6k6OsvZFB3epuc5" alt="Image" width="740" height="208" loading="lazy"></p>
<ol start="3">
<li><p>Create a record set and leave the name empty (it will default to example.com). Then you’d need to:</p>
</li>
<li><p>Set the type to “A — IPv4 address”</p>
</li>
<li>Respond with “Yes” to “Alias” and set the alias target to your CloudFront distribution URL.</li>
<li><p>Keep the routing policy as “Simple” and, based on your budget and needs, enable or disable “Evaluate Target Health”.</p>
</li>
<li><p>Repeat step 3 for type “AAAA - IPv6 address” if you enabled your CloudFront distribution to be IPv6 compatible. If you followed this tutorial, IPv6 was enabled by default.</p>
</li>
</ol>
<p>Note that DNS propagation can take up to <a target="_blank" href="https://www.youtube.com/watch?v=Gr8RzCZWh5M">72 hours</a>, although it should be normally updated within a few hours or faster. If you previously set any other DNS records (like MX for work emails), you’ll have to reset them in Route53.</p>
<h4 id="heading-setting-up-your-www-subdomain">Setting up your WWW subdomain</h4>
<p>Congrats for getting this far! I’m sorry to let you know that now you have to repeat all the previous three steps. Yes, you heard that right, because of the elusive way the Internet works, <code>www</code> is not something included as a holistic component of HTTP.</p>
<p>It is important to point out that it’s really not mandatory to add a www subdomain to your website and you can safely proceed to the next step <em>if</em> you’re fine with your end users not being able to access your website via <code>www.example.com</code>. I was a bit pedantic about it and I simply had to add the www subdomain.</p>
<p>Notes:</p>
<ol>
<li>Redo only the steps for S3, CloudFront and Route53, you don’t have to (and can’t) go to <a target="_blank" href="https://namecheap.pxf.io/c/1243704/386170/5618">Namecheap</a> to buy <code>www.example.com</code>.</li>
<li>For all the fields where you were asked to put <code>example.com</code>, now put <code>www.example.com</code>.</li>
</ol>
<p>If you wonder whether by creating S3 buckets, it means you are required to deploy your static files to both of them, the answer is no, you don’t have to do that. When you activate “Static website hosting” for your second S3 bucket, select “Redirect requests” instead of “Use this bucket to host a website”:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/sLTt9ve75kNIECFDmnM1dNrnWhd4wtPUTIau" alt="Image" width="750" height="734" loading="lazy"></p>
<h4 id="heading-ssl-certificate">SSL Certificate</h4>
<p>LetsEncrypt is one of the best things that happened to the Internet in the last couple of years. They have democratised access to SSL certificates and this is a huge accomplishment, kudos to them! If LetsEncrypt was so helpful to you, consider making a <a target="_blank" href="https://letsencrypt.org/donate/">donation</a>.</p>
<p>This step is crucial and also the hardest in the whole tutorial, so proceed carefully. You could use either your own machine or a Linux server to generate the certificate, but I chose the former option, it’s simpler and less expensive.</p>
<ol>
<li>Head to the <a target="_blank" href="https://github.com/dlapiduz/certbot-s3front">certbot-s3front</a> repo and install the tool. You need to have Python and pip installed.</li>
<li>Follow their instructions, but (1) skip the S3 and CloudFront parts, you had already done that and (2) set “example.com,<a target="_blank" href="http://www.example.com/">www.example.com</a>" as the value for the “-d” (domain) parameter. Read more about this on the LE <a target="_blank" href="https://community.letsencrypt.org/t/certification-is-not-working-in-firefox-your-connection-is-not-secure/43090/6?u=paulrberg">forum</a>.</li>
<li>After you successfully generate your SSL certificate, you could optionally enable “Redirect HTTP to HTTPS” on your naked domain (that is, “example.com”) CloudFront distribution. Don’t do this for “www”, as it’ll redirect to your naked domain anyway.</li>
<li>Make sure to backup your <code>/etc/letsencrypt/live/example.com</code> certificate(s).</li>
</ol>
<p>Notes:</p>
<ol>
<li>Due to <a target="_blank" href="https://github.com/dlapiduz/certbot-s3front/issues/70">mysterious reasons</a>, I couldn’t make certbot’s authentication work by setting the “AWS_ACCESS_KEY_ID” and “AWS_SECRET_ACCESS_KEY” environment variables. This might be caused by the fact that I have several different profiles in my <code>~/.aws/credentials</code>, but I'm not sure. To avoid a "NoCredentialsError", just temporarily set a "default" profile and certbot will pick that up.</li>
<li>If you’re unlucky like me and you get a further “IAMCertificateId” error, check out <a target="_blank" href="https://github.com/dlapiduz/certbot-s3front/issues/76">this solution</a>.</li>
</ol>
<h4 id="heading-acm">ACM</h4>
<p>Shortly after this article was published, a lot of people jumped in to say that it’d be much easier to use the AWS Certificate Manager (ACM) for generating certificates. No need to think about renewals, but it means you’re locked in with AWS.</p>
<h4 id="heading-bottlenecks">Bottlenecks</h4>
<ol>
<li>No server logic: This tutorial is only applicable to static websites, so you cannot run any backend logic using a Node.js module like <a target="_blank" href="https://expressjs.com/">ExpressJS</a>. For that, you can either spin up an EC2 instance, write Lambda functions or use Docker via ECS/ Kubernetes.</li>
<li>LetsEncrypt certificates expire in 90 days: You can solve this in two ways. Firstly, you could set a reminder in your calendar, which I admit is suboptimal, but I’m in an experimentation phase so I'm not bothered by a bit of manual work. Secondly, you could set a cron job, but you need a Linux server and use the “ — renew-by-default — text” options when interacting with certbot.</li>
<li>Rich link previews can be a mess: This could be a problem specific to my Hugo theme, but I also think everyone wants to have a proper preview image and description when sharing their website links. <a target="_blank" href="https://github.com/calintat/minimal/issues/61#issuecomment-449999181">Here’s</a> how I managed to do it.</li>
</ol>
<h4 id="heading-wrap-up">Wrap-Up</h4>
<p>Congrats, you now have a really cheap but still highly flexible static website! Billing stats for 100 - 1000 monthly active visitors and fairly frequent S3 deployments are <strong>between $1 and $2</strong>, so this is a steal! For usage way beyond that, you may need to upgrade your AWS components, but this is outside the scope of this tutorial.</p>
<p>If you’re an experienced developer interested to replicate this tutorial on multiple AWS accounts, you may want to check out <a target="_blank" href="https://terraform.io/">Terraform</a>. It’s a super duper cool Infrastructure-as-a-Service tool which you can use to define your S3, CloudFront and Route53 as code snippets. Isn’t technology so dang amazing?</p>
<p>Hope you find this tutorial helpful! Find me on <a target="_blank" href="https://twitter.com/PaulRBerg"><strong>Twitter</strong></a> or <a target="_blank" href="https://keybase.io/PaulRBerg"><strong>Keybase</strong></a> if you want to chat.</p>
<h3 id="heading-credits">Credits</h3>
<ul>
<li><a target="_blank" href="https://amazon.com/">Amazon</a> for the AWS, S3, CloudFront and Route53 logos</li>
</ul>
<p><em>Originally published on <a target="_blank" href="https://paulrberg.com/post/static-website-aws/s3.png">paulrberg.com</a></em></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
