Deploying an Angular App to AWS S3 with CloudFront
This guide walks through the process of hosting an Angular application (version 2 or newer) on AWS S3 with CloudFront as the content delivery layer.
Before diving into the deployment steps, let's clarify the AWS services involved:
- AWS S3 is a cloud storage service commonly used for storing content, media, or enabling static web hosting.
- CloudFront is AWS's content delivery network (CDN). It acts as an endpoint to distribute content from servers or S3 buckets at high speed. It supports various origins, including Elastic Load Balancers and S3 buckets.
- ACM is AWS's SSL certificate provider. Public SSL/TLS certificates issued through ACM are free; you only pay for the AWS resources you create to run your application.
- Route53 is a domain routing service where you can configure NS records, A records, or CNAMEs for domain traffic. It offers a list of endpoints, allowing you to route traffic directly to an S3 bucket or through CloudFront.
With the terminology clear, let's move to the actual implementation.
Here's how the complete hosting flow works: 👇🏻
Follow these steps to build the architecture shown above:
Create an S3 bucket with the necessary configuration. The bucket name should match your domain name exactly. For instance, if your domain is
www.example.com, the bucket should be namedwww.example.com. Detailed instructions for setting up an S3 bucket for website hosting are available here: https://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteAccessPermissionsReqd.html
Generate the Angular production build using
ng build.Upload the contents of the
distfolder to the S3 bucket.Configure S3 website hosting by setting both the index document and the error document to
index.html. In Angular, all route requests must be directed toindex.html, so this configuration is essential.
-
Set up the bucket policy under Permissions → Block Public Access and Permissions → Bucket Policy.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": [ "s3:GetObject" ], "Resource": [ "arn:aws:s3:::example.com/*" ] } ] } Create a CloudFront distribution by following these steps: https://aws.amazon.com/premiumsupport/knowledge-center/cloudfront-https-requests-s3/
Set up the Route53 entry for your domain.

The Bucket Policy configuration is shown below:
Does that complete everything?
Not quite...
One critical detail remains. Angular uses client-side routing and URL rewriting. A single page handles all routing, with index.html as the root file responsible for every route. The setup described above works fine while you navigate within the application without refreshing. However, if the user hits refresh, S3 will return an "Access Denied" error.
The reason is that S3 doesn't recognize the route opened after a reload or when the URL is accessed in a new tab. You must instruct S3 to serve index.html for these routes. Without this, any new route opened directly will result in a 403 (access denied) error. To resolve this, configure CloudFront to redirect 403 error responses to index.html.
Navigate to CloudFront → select your distribution → go to Error pages → click Create Custom Error Response.
With that, the configuration is finally complete 😁👍🏻
Feel free to contact me on @aviboy2006 if you need any clarification or run into issues. You can also refer to the automated Python deployment script for Angular build deployment.
References:



