DEV Community

Andrii Shykhov
Andrii Shykhov

Posted on • Originally published at Medium

Static Website on S3 with CloudFront and Route 53

Image from Freepik

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

  1. Better Performance: CloudFront caches content in edge locations globally, reducing latency for users.

  2. Security: S3 bucket access can be restricted to CloudFront, preventing direct public access.

  3. Custom Domain: Route 53 allows seamless domain management instead of using an S3 bucket URL.

  4. HTTPS Support: CloudFront provides free SSL/TLS certificates via ACM, securing website traffic.

  5. Reduced Costs: CloudFront reduces data transfer costs compared to direct S3 access.

  6. 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}' 
Enter fullscreen mode Exit fullscreen mode

Infrastructure schema
Infrastructure schema

Site example
Site example

Site error handling
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.

 git clone git@gitlab.com:Andr1500/s3_static_website.git 
Enter fullscreen mode Exit fullscreen mode

2.Create ACM certificate.

 aws acm request-certificate \ --domain-name 'yourdomain.com' \ --validation-method DNS \ --idempotency-token 'cert_request' --region us-east-1 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

4.Retrieve outputs of the CloudFormation stack.

 aws cloudformation describe-stacks \ --stack-name static-website \ --query "Stacks[0].Outputs" --output json 
Enter fullscreen mode Exit fullscreen mode

5.Upload static website files.

 export BUCKET_NAME=<Bucket name> aws s3 cp site_files s3://$BUCKET_NAME/ --recursive 
Enter fullscreen mode Exit fullscreen mode

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 "/*" 
Enter fullscreen mode Exit fullscreen mode

7.Cleanup.

 aws s3 rm s3://$BUCKET_NAME --recursive aws cloudformation delete-stack --stack-name static-website aws acm delete-certificate --certificate-arn <certificate_arn> 
Enter fullscreen mode Exit fullscreen mode

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. 🙂

Top comments (0)