Host Your Portfolio on AWS: S3, CloudFront, Route 53 & CI/CD
A step-by-step guide for seamlessly hosting a portfolio website on AWS with S3, CloudFront, Route 53, CodeBuild & CodePipeline for automated CI/CD deployment.
Creating a Static Website on AWS
Option 1: Purchasing and Registering a New Domain Directly Through AWS
Option 2: Using an Existing Domain from Another Provider
4. AWS Certificate Manager (ACM)
Create the 'root' CloudFront Distribution
Create the 'redirect' CloudFront Distribution
Create an Alias Record for the Root Domain (non-www)
Create an Alias Record for the www-prefixed Domain
This section explains how key components of AWS work together for a static site. If you’re already familiar with these AWS services, feel free to skip ahead to the Tutorial section.
example.com
, navigating to www.example.com
will result in a 404 error. To resolve this, you can create a second S3 bucket specifically for www.example.com
and configure it to redirect requests to the root bucket (example.com
), which hosts your website files.FirstnameLastname.com
is memorable, professional, and typically costs around $10-$15 per year. You will create and configure a Hosted Zone for the domain that you use.A public hosted zone is a container that holds information about how you want to route traffic on the internet for a specific domain, such as example.com, and its subdomains (acme.example.com, zenith.example.com).
- Navigate to the Route 53 service in the AWS Console.
- You have two options for setting up your domain.
- From the left-hand menu, under the Domains section, click on Registered Domains, then choose Register Domains to start the registration process.
- After choosing, purchasing, and registering your new domain, it will appear in the Hosted Zones section once approved, where you can manage its DNS settings.
- From the left-hand menu, click on Hosted Zones, then choose Create Hosted Zone to create a new hosted zone.
- For the Domain name, enter your existing domain without a www-prefix (e.g.,
example.com
). Adding a description is optional. - For Type, choose Public hosted zone, and then click Create hosted zone to complete the setup.
- Back in the Hosted Zones section, navigate to your newly created zone, and take note of the Nameservers in the NS record. They should look something like this:
ns-620.awsdns-13.net.
ns-156.awsdns-19.com.
ns-1191.awsdns-20.org.
ns-1922.awsdns-48.co.uk.
Note: Your domain provider might issue a warning against changing Nameservers. This can be safely ignored. The changes can take up to 24 hours to propagate (but usually only take a few minutes). Once complete, all future DNS settings for this domain will be managed through AWS in the hosted zone you created.
npm run build
quickly generates all the necessary production assets. There are numerous free resources available to download portfolio templates if you need a starting point, or you can clone my repository to access files from my portfolio.index.html
file displaying 'Hello World' and a 404.html
file displaying '404 Not Found.'Note: If you’d prefer your website to redirect to a 'www' prefix instead, simply reverse these roles.
- Navigate to the S3 service in the AWS Console and ensure you're in your preferred region.
- Create an S3 Bucket for the root bucket.
- Click Create bucket to set up a General purpose S3 bucket and name it after your domain, without the 'www' prefix (e.g.,
example.com
) - Ensure ACLs disabled (recommended) is selected.
- Uncheck 'Block all public access' and confirm the acknowledgement warning to ensure the bucket is accessible to the public and internet-facing.
- Enable Bucket Versioning and select Server-side encryption with Amazon S3 managed keys (SSE-S3) for encryption.
- Disable Bucket Key and Object Lock (in Advanced settings) and click Create bucket.
- Upload Website Assets
- Access your bucket and use the uploader to add your website files to the S3 bucket.
- Ensure your
index.html
and404.html
files are located in the root directory of the bucket.
- Enable Static Website Hosting
- Go to the Properties tab in the bucket, scroll to the bottom, edit Static website hosting to select Enable, and ensure that Host a static website is selected.
- Specify the Index document, which for this tutorial is
index.html
. Additionally, set the Error document to404.html
. and press Save Changes. - Take note of the Bucket website endpoint at the bottom of the Properties page (which you will use to test the website after the next step).
- Add a Bucket Policy
- Go to the Permissions tab and click Edit to add the necessary Bucket Policy (below) which allows public access. Replace
BUCKET_NAME
in the policy with your actual bucket name (e.g.,example.com
) and press Save changes.
- Navigate to the root bucket website endpoint from the previous step. If everything is set up correctly, you should see 'Hello world' (or your own index page).
- Create another S3 Bucket for the redirect bucket.
- Click Create bucket to set up a General purpose S3 bucket and name it after your domain, with the 'www' prefix (e.g.,
www.example.com
) - Ensure ACLs disabled (recommended) is selected.
- Uncheck 'Block all public access' and confirm the acknowledgement warning to ensure the bucket is accessible to the public and internet-facing.
- Disable Bucket Versioning and select Server-side encryption with Amazon S3 managed keys (SSE-S3) for encryption.
- Disable Bucket Key and Object Lock (in Advanced settings) and click Create bucket.
- Do not upload Website Assets
- This bucket will not contain any assets. Its only purpose is to redirect to your root bucket.
- Enable Static Website Hosting
- Access your bucket and go to the
Properties
tab in the bucket, scroll to the bottom, edit Static website hosting to select Enable, and ensure that Redirect requests for an object is selected. - For the Host name, enter the name of your non-www domain that you will redirect to (e.g.,
example.com
) - For the Protocol select http and press Save Changes.
- Take note of the Bucket website endpoint at the bottom of the
Properties
page (which you will use to test the website next).
Note: A Bucket Policy isn't required for this bucket.
- At this point, if you navigate to the rucket website endpoint it should redirect you to your root domain (e.g.,
example.com
) over an http connection. You should receive a403 Error
, which you'll address later when setting up CloudFront.
- Navigate to the Certificate Manager service in the AWS Console and make sure your region is set to
US-East-1
, as this is required for global certificates. - In Certificate Manager click Request, confirm that Request a public certificate is selected, and click Next.
- For the Fully qualified domain name (FQDN), enter the hosted zone domain name you created earlier in Route 53 (e.g.,
example.com
) - Click Add another name to this certificate and enter the www-prefixed version of your domain (e.g.,
www.example.com
). - Set the Validation method to DNS validation and choose RSA 2048 for the Key algorithm.
- Click Request to finalize the certificate request. This SSL certificate will be used in the next step for setting up the CloudFront distributions.
Note: It will take a few minutes for your certificate to validate and be issued.
- If you purchased and registered your domain through AWS, validation will be automatic.
- If you're using an external domain managed through AWS, add the CNAME records to your Hosted Zone in Route 53. You can do this manually by entering the CNAME names and values, or by opening the certificate and selecting Create Records in Route 53 under the Domains section.
Note: If you’d prefer your website to redirect to a 'www' prefix instead, simply reverse these roles.
- Navigate to the CloudFront service in the AWS Console.
- Create a CloudFront distribution for the root bucket.
- Click Create distribution and for the Origin domain select the root S3 bucket you created earlier (e.g.,
example.com.s3.amazonaws.com
). - After selecting your S3 bucket, choose Use website endpoint.
- Leave the rest of the settings in the Origin section as default.
- Configure Default Cache Behavior
- Under Viewer protocol policy choose Redirect HTTP to HTTPS.
- Under Cache key and origin requests ensure that CachingOptimized is selected.
- Leave the rest of the settings as default.
- Set Web Application Firewall (WAF)
- Set the WAF to Do not enable security protections.
- Configure Domain and SSL
- In the final Settings section, set the Alternate Domain Name (CNAME) to the non-www domain you created earlier (e.g.,
example.com
). - Select the SSL certificate you requested in the previous step and for the Security Policy choose TLSv1.2_2021.
- Under Supported HTTP versions select both HTTP/2 and HTTP/3.
- Leave the rest of the settings as default, and click Create distribution.
- In the General tab of the 'root' distribution, note the ARN and Domain name, as you'll need these in the next section.
- Create another CloudFront distribution for the redirect bucket.
- Click Create distribution and for the Origin domain select the redirect S3 bucket you created earlier (e.g.,
www.example.com.s3.amazonaws.com
). - After selecting your S3 bucket, choose Use website endpoint.
- Leave the rest of the settings in the Origin section as default.
- Configure Default Cache Behavior
- Under Viewer protocol policy choose Redirect HTTP to HTTPS.
- Under Cache key and origin requests ensure that CachingOptimized is selected.
- Leave the rest of the settings as default.
- Set Web Application Firewall (WAF)
- Set the WAF to Do not enable security protections.
- Configure Domain and SSL
- In the final Settings section, set the Alternate Domain Name (CNAME) to the www-prefixed domain you created earlier (e.g.,
www.example.com
). - Select the SSL certificate you requested in the previous step and for the Security Policy choose TLSv1.2_2021.
- Under Supported HTTP versions choose both HTTP/2 and HTTP/3.
- Leave the rest of the settings as default, and click Create distribution.
- In the General tab of the 'redirect' distribution, note the Domain name, as you'll need this in the next section.
- Navigate back to the S3 service in the AWS Console and ensure you're in the region you created your buckets in.
- Select the root bucket (e.g.,
example.com
) and go to the Permissions tab. - Click Edit to add the Bucket policy (below) in order to grant CloudFront permission to read the bucket’s contents.
- Replace
BUCKET_NAME
with your root bucket name (e.g.,example.com
) andACCOUNT_NUMBER
/DISTRIBUTION_ID
with the values from the ARN of your 'root' CloudFront distribution.
- After updating the bucket policy, confirm that the CloudFront distributions have finished deploying. Once deployments are complete, test their functionality by accessing the CloudFront domain names from the previous steps.
- Your 'root' CloudFront distribution should load your HTTPS-secured webpage.
- Your 'redirect' CloudFront distribution should redirect to your HTTPS root domain (e.g.,
https://example.com
).
Note: This redirection will not fully function until the A records are set in Route 53 in the next step. You just want to make sure it redirects correctly.
- Navigate back to the Route 53 service in the AWS Console.
- From the left-hand menu, click on Hosted Zones, then select the Hosted Zone you created earlier (e.g.
example.com
). - Click Create record to create a new record.
- Under Record name leave the subdomain blank.
- For the Record type select A - Routes traffic to an IPv4 address and some AWS resources.
- Choose Alias and under Route traffic to select:
- Choose endpoint: Alias to CloudFront distribution.
- Choose distribution: select the CloudFront distribution that serves your root domain (e.g.,
example.com (NON-WWW-CLOUDFRONT-DOMAIN.cloudfront.net)
).
- Leave the default settings for other options, and click Create records.
- Click Create record to create another new record.
- Under Record name for the subdomain enter
www
. - For the Record type select A - Routes traffic to an IPv4 address and some AWS resources.
- Choose Alias and under Route traffic to select:
- Choose endpoint: Alias to CloudFront distribution.
- Choose distribution: select the CloudFront distribution that serves your redirect domain (e.g.,
www.example.com (WWW-CLOUDFRONT-DOMAIN.cloudfront.net)
).
- Leave the default settings for other options, and click Create records.
Note: It will probably take 5-10 minutes for the changes to fully propagate, and your website endpoints to be accessible.
- If you do not plan to update it often or prefer to update files manually in the root S3 bucket, you can stop here.
- To set up a continuous integration/continuous deployment (CI/CD) pipeline that automates future code updates from a GitHub repository, continue to the next section of the tutorial.
Note: This section assumes your portfolio website files are stored in a personal GitHub repository. If they aren’t yet, take a moment now to create a new repository and commit your files to it.
- Navigate to the IAM service in the AWS Console.
- Create a new Role for the CodeBuild service.
- In the left navigation, choose Roles, then click Create role.
- Select AWS service, under Use case select CodeBuild, and click Next.
- Search for and select the following policy:
CloudFrontFullAccess
, then click Next. - Enter
CodeBuildServiceRole
as the name for the role, then press Create role.
Note: This role will allow CodeBuild to have access to CloudFront for the purpose of creating invalidations.
- Navigate to the CodeBuild service in the AWS Console and ensure you're in your preferred region.
- Click Create Project and for Project name enter
Build-GitHubPortfolio
. - In the Source section:
- Under Source provider select GitHub.
- For Credential select Default Source Credential, then Manage default source credential.
- For Credential Type, select GitHub App and create a new GitHub connection. Name the connection (e.g.,
GitHub-CI-CD-Connection
). - Click Connect to GitHub to authorize. After authorization, choose Install a new app. Under Only select repositories, select your portfolio repository (or choose All repositories if you prefer full access to all your repositories).
- Once the app is installed, click Connect.
- Select your new
GitHub-CI-CD-Connection
under Connection and click Save. - Choose your repository from the dropdown.
- In the Primary source webhook events section:
- Make sure Rebuild every time a code change is pushed to this repository is not selected.
- You'll handle the rebuild trigger through CodePipeline.
- In the Environment section:
- For Provisioning model, select On-demand.
- For Environment image, choose Managed image.
- For Compute, select EC2.
- For Operating system, choose Amazon Linux.
- For Runtime, select Standard.
- For Image, choose aws/codebuild/amazonlinux2-x86_64-standard:5.0.
- For Image version, set to Always use the latest image for this runtime version.
- For Service role, select Existing service role and choose
CodeBuildServiceRole
, which you just created. - Make sure Allow AWS CodeBuild to modify this service role so it can be used with this build project is selected.
- In the Buildspec section:
- Select Insert build commands, click Switch to editor, and enter the following build commands.
- Replace
YOUR_DISTRIBUTION_ID
with theDistribution_ID
from the ARN of your 'root' CloudFront distribution.
Note: This buildspec is tailored for building a Next.js project. You may need to adjust it slightly if using another framework, like Gatsby or Create React App.
- Click the Start build button. If everything is configured correctly, you should see a green checkmark and a Succeeded message under the Status section after a few minutes.
- Navigate to the CodePipeline service in the AWS Console and ensure you're in your preferred region.
- Click Create pipeline to start the setup process and select Build custom pipeline.
- Choose Pipeline settings:
- For Pipeline name enter
Pipeline-GitHubPortfolio
. - For Execution mode select Queued (Pipeline type V2 required).
- Ensure New service role is selected, keeping the default Role name.
- Check the box for Allow AWS CodePipeline to create a service role so it can be used with this new pipeline, then click Next.
- Add Source stage:
- For Source provider select GitHub (via GitHub App).
- Select
GitHub-CI-CD-Connection
, which you created earlier, and select your portfolio repository. - Select
main
as your Default branch (or your actual default branch, if different). - For Output artifact format select CodePipeline default.
- Check the box for Enable automatic retry on stage failure.
- For the Trigger settings:
- For Trigger Type select Specify filter.
- For Event Type select Push.
- For Filter Type select Branch.
- For Branches under Include enter
main
(or your default branch).
- Click Next.
- Add Build Stage:
- Select Other build providers.
- Choose AWS CodeBuild from the dropdown, then select
Build-GitHubPortfolio
, which you created earlier. - For Build type select Single build.
- Ensure the Region is set to your pipeline region.
- For Input artifacts, ensure SourceArtifact is selected.
- Check the box for Enable automatic retry on stage failure, then click Next.
- Add Deploy Stage:
- For Deploy provider select Amazon S3.
- Ensure the Region is set to your pipeline region.
- For Input artifacts, ensure BuildArtifact is selected.
- For Bucket name select your root bucket (e.g.,
example.com
). - Leave S3 object key blank.
- Check the box for Extract file before deploy.
- Ensure Configure automatic rollback on stage failure is checked, then click Next.
- Finalize the Pipeline
- Review the configuration, then click Create pipeline.
- Monitor the initial pipeline run to confirm each stage succeeds.