Static Website on S3 with CloudFront and Route 53
Introduction This project demonstrates how AWS services like S3, CloudFront, and Route 53 can be used to host a static website. CloudFormation automates the infrastructure provisioning. The example site has a simple structure with index.html and error.html files. Cost Estimation The cost of this solution depends on usage, but approximate monthly expenses include: S3 Storage: $0.023/GB. CloudFront Data Transfer: $0.085/GB for North America and Europe. Route 53 Domain Registration: ~$12 per year (varies by domain TLD). Route 53 Hosted Zone: $0.50 per month for the first 25 hosted zones. CloudFront Requests: $0.0075 per 10,000 HTTP/HTTPS requests. For a small static site with low traffic, the cost can be as low as a few dollars per month. Advantages of CloudFront and Route 53 Over Direct S3 Access Better Performance: CloudFront caches content in edge locations globally, reducing latency for users. Security: S3 bucket access can be restricted to CloudFront, preventing direct public access. Custom Domain: Route 53 allows seamless domain management instead of using an S3 bucket URL. HTTPS Support: CloudFront provides free SSL/TLS certificates via ACM, securing website traffic. Reduced Costs: CloudFront reduces data transfer costs compared to direct S3 access. Error Handling: Custom error pages can be displayed instead of raw S3 errors. Project structure with infrastructure schema and site example Here is configuration in infrastructure/root.yaml CloudFormation template: AWSTemplateFormatVersion: '2010-09-09' Description: CFN template for S3 Static Website with CloudFront and Route53 Parameters: HostedZoneName: Type: String Default: '' HostedZoneId: Type: String Default: '' AcmCertificateArn: Type: String Default: '' Resources: StaticWebsiteBucket: Type: 'AWS::S3::Bucket' Properties: BucketName: !Sub 'static-website-${AWS::AccountId}' WebsiteConfiguration: IndexDocument: index.html ErrorDocument: error.html BucketPolicy: Type: 'AWS::S3::BucketPolicy' Properties: Bucket: !Ref StaticWebsiteBucket PolicyDocument: Statement: - Action: 's3:GetObject' Effect: Allow Resource: !Sub 'arn:${AWS::Partition}:s3:::${StaticWebsiteBucket}/*' Principal: Service: 'cloudfront.amazonaws.com' OriginAccessControl: Type: 'AWS::CloudFront::OriginAccessControl' Properties: OriginAccessControlConfig: Name: 'OAC-StaticWebsite' OriginAccessControlOriginType: 's3' SigningBehavior: 'always' SigningProtocol: 'sigv4' CloudFrontDistribution: Type: 'AWS::CloudFront::Distribution' Properties: DistributionConfig: Enabled: true DefaultRootObject: index.html Aliases: - !Ref HostedZoneName Origins: - Id: S3Origin DomainName: !GetAtt StaticWebsiteBucket.RegionalDomainName OriginAccessControlId: !Ref OriginAccessControl S3OriginConfig: OriginAccessIdentity: '' DefaultCacheBehavior: TargetOriginId: S3Origin ViewerProtocolPolicy: 'redirect-to-https' AllowedMethods: ['GET', 'HEAD'] CachedMethods: ['GET', 'HEAD'] ForwardedValues: QueryString: false Cookies: Forward: none CustomErrorResponses: - ErrorCode: 403 ResponsePagePath: '/error.html' ResponseCode: 200 - ErrorCode: 404 ResponsePagePath: '/error.html' ResponseCode: 200 ViewerCertificate: AcmCertificateArn: !Ref AcmCertificateArn SslSupportMethod: 'sni-only' PriceClass: PriceClass_100 Route53RecordSet: Type: 'AWS::Route53::RecordSet' Properties: HostedZoneId: !Ref HostedZoneId Name: !Ref HostedZoneName Type: 'A' AliasTarget: DNSName: !GetAtt CloudFrontDistribution.DomainName HostedZoneId: 'Z2FDTNDATAQYW2' # the hosted zone ID applicable only for CloudFront Outputs: CloudFrontURL: Description: 'CloudFront Distribution URL' Value: !Sub 'https://${CloudFrontDistribution.DomainName}' Infrastructure schema Site example Site error handling Prerequisites Ensure the following prerequisites are in place: An AWS account with sufficient permissions to create and manage resources. The AWS CLI installed on the local machine. A registered domain in Route 53. Deployment 1.Clone the repository.

Introduction
This project demonstrates how AWS services like S3, CloudFront, and Route 53 can be used to host a static website. CloudFormation automates the infrastructure provisioning. The example site has a simple structure with index.html
and error.html
files.
Cost Estimation
The cost of this solution depends on usage, but approximate monthly expenses include:
S3 Storage: $0.023/GB.
CloudFront Data Transfer: $0.085/GB for North America and Europe.
Route 53 Domain Registration: ~$12 per year (varies by domain TLD).
Route 53 Hosted Zone: $0.50 per month for the first 25 hosted zones.
CloudFront Requests: $0.0075 per 10,000 HTTP/HTTPS requests.
For a small static site with low traffic, the cost can be as low as a few dollars per month.
Advantages of CloudFront and Route 53 Over Direct S3 Access
Better Performance: CloudFront caches content in edge locations globally, reducing latency for users.
Security: S3 bucket access can be restricted to CloudFront, preventing direct public access.
Custom Domain: Route 53 allows seamless domain management instead of using an S3 bucket URL.
HTTPS Support: CloudFront provides free SSL/TLS certificates via ACM, securing website traffic.
Reduced Costs: CloudFront reduces data transfer costs compared to direct S3 access.
Error Handling: Custom error pages can be displayed instead of raw S3 errors.
Project structure with infrastructure schema and site example
Here is configuration in infrastructure/root.yaml
CloudFormation template:
AWSTemplateFormatVersion: '2010-09-09'
Description: CFN template for S3 Static Website with CloudFront and Route53
Parameters:
HostedZoneName:
Type: String
Default: ''
HostedZoneId:
Type: String
Default: ''
AcmCertificateArn:
Type: String
Default: ''
Resources:
StaticWebsiteBucket:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: !Sub 'static-website-${AWS::AccountId}'
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: error.html
BucketPolicy:
Type: 'AWS::S3::BucketPolicy'
Properties:
Bucket: !Ref StaticWebsiteBucket
PolicyDocument:
Statement:
- Action: 's3:GetObject'
Effect: Allow
Resource: !Sub 'arn:${AWS::Partition}:s3:::${StaticWebsiteBucket}/*'
Principal:
Service: 'cloudfront.amazonaws.com'
OriginAccessControl:
Type: 'AWS::CloudFront::OriginAccessControl'
Properties:
OriginAccessControlConfig:
Name: 'OAC-StaticWebsite'
OriginAccessControlOriginType: 's3'
SigningBehavior: 'always'
SigningProtocol: 'sigv4'
CloudFrontDistribution:
Type: 'AWS::CloudFront::Distribution'
Properties:
DistributionConfig:
Enabled: true
DefaultRootObject: index.html
Aliases:
- !Ref HostedZoneName
Origins:
- Id: S3Origin
DomainName: !GetAtt StaticWebsiteBucket.RegionalDomainName
OriginAccessControlId: !Ref OriginAccessControl
S3OriginConfig:
OriginAccessIdentity: ''
DefaultCacheBehavior:
TargetOriginId: S3Origin
ViewerProtocolPolicy: 'redirect-to-https'
AllowedMethods: ['GET', 'HEAD']
CachedMethods: ['GET', 'HEAD']
ForwardedValues:
QueryString: false
Cookies:
Forward: none
CustomErrorResponses:
- ErrorCode: 403
ResponsePagePath: '/error.html'
ResponseCode: 200
- ErrorCode: 404
ResponsePagePath: '/error.html'
ResponseCode: 200
ViewerCertificate:
AcmCertificateArn: !Ref AcmCertificateArn
SslSupportMethod: 'sni-only'
PriceClass: PriceClass_100
Route53RecordSet:
Type: 'AWS::Route53::RecordSet'
Properties:
HostedZoneId: !Ref HostedZoneId
Name: !Ref HostedZoneName
Type: 'A'
AliasTarget:
DNSName: !GetAtt CloudFrontDistribution.DomainName
HostedZoneId: 'Z2FDTNDATAQYW2' # the hosted zone ID applicable only for CloudFront
Outputs:
CloudFrontURL:
Description: 'CloudFront Distribution URL'
Value: !Sub 'https://${CloudFrontDistribution.DomainName}'
Prerequisites
Ensure the following prerequisites are in place:
- An AWS account with sufficient permissions to create and manage resources.
- The AWS CLI installed on the local machine.
- A registered domain in Route 53.
Deployment
1.Clone the repository.
git clone git@gitlab.com:Andr1500/s3_static_website.git
2.Create ACM certificate.
aws acm request-certificate \
--domain-name 'yourdomain.com' \
--validation-method DNS \
--idempotency-token 'cert_request' --region us-east-1
3.Fill in all necessary Parameters in infrastructure/root.yaml CloudFormation template, and create the CloudFormation stack.
aws acm list-certificates --region us-east-1
aws route53 list-hosted-zones-by-name --dns-name 'yourdomain.com' --query 'HostedZones[0].Id' --output text
aws cloudformation create-stack \
--stack-name static-website \
--template-body file://infrastructure/root.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--disable-rollback
4.Retrieve outputs of the CloudFormation stack.
aws cloudformation describe-stacks \
--stack-name static-website \
--query "Stacks[0].Outputs" --output json
5.Upload static website files.
export BUCKET_NAME=
aws s3 cp site_files s3://$BUCKET_NAME/ --recursive
6.Update files to the S3 bucket and invalidate CloudFormation cache.
aws s3 sync site_files s3://$BUCKET_NAME
aws cloudfront list-distributions --query "DistributionList.Items[*].[Id,DomainName]"
aws cloudfront create-invalidation --distribution-id YOUR_DISTRIBUTION_ID --paths "/*"
7.Cleanup.
aws s3 rm s3://$BUCKET_NAME --recursive
aws cloudformation delete-stack --stack-name static-website
aws acm delete-certificate --certificate-arn
Conclusion
By leveraging AWS services like S3, CloudFront, and Route 53, a static website can be deployed and accessed via a custom domain. This solution provides a scalable, cost-effective, and secure static site hosting infrastructure without the complexity of traditional web servers.
If you found this post helpful and interesting, please click the reaction button below to show your support. Feel free to use and share this post.