There are many ways of hosting websites. If you want to host on AWS it's not as easy as it could be, but this example will get you started.
Deploying a "static" website on AWS is surprisingly tricky - it requires managing S3, CloudFront, the security between them, Route 53, and more. This example project helps you get started using AWS CDK to deploy a website.
If you want to do this with "vanilla" CloudFormation, see the (much!) older version of this project in the CloudFormation branch.
This example is part of a collection of CDK examples - others are as follows:
- CDK barebones template - Base project for any TypeScript app using CDK for deployment to AWS. Try this first if you are getting started with CDK.
- CDK serverless template - Adds a Lambda Function resource; source code + build for the Lambda Function; unit + in-cloud integration tests
- CDK full website template - An extension of this project that is a real working demo of a production-ready website, including TLS certificates, DNS, Github Actions Workflows, multiple CDK environments (prod vs test vs dev). Head straight to this project if you already familiar with CDK and deploying websites to AWS.
This example deploys an S3 Bucket to hold your website content, and a CloudFront Distribution to handle web requests. CloudFront is actually a Content Delivery Network (CDN) and so also provides a location-oriented cache.
The example deploys these two resources as a CDK App, which in turn uses AWS CloudFormation under the covers, to provide an automated infrastructure-as-code process.
During the deployment process your site's content is also uploaded, courtesy of CDK's BucketDeployment Construct.
If you want to use your own upload mechanism then remove the BucketDeployment from the code.
This example does not include setting up a custom hostname for the site - it uses the default provided by cloudfront. To use a custom hostname see my CDK full website template project instead.
Mostly this example uses the default configuration for S3 and CloudFront provided by CDK or the services themselves - including "good practice" security. A few small tweaks related to CloudFront are:
- Redirect http requests to https (NB: this is good for web sites but not always a great idea for web APIs - see this link for why)
- Enable http version 2 and version 3
- Set the default root object (for requests to
/) as index.html
Please see the prerequisites of the CDK barebones template project - they are the same as for this one.
After cloning this project to your local machine, run the following, which uses the default CloudFormation stack name of cdk-basic-website-template:
$ npm install && npm run deployCloudFront distributions take several minutes to provision - so don't be surprised that the deployment requests takes a while.
If successful, the end result will look something like this:
cdk-basic-website-template: creating CloudFormation changeset...
✅ CdkBasicWebsiteTemplate (cdk-basic-website-template)
✨ Deployment time: 610.9s
Outputs:
CdkBasicWebsiteTemplate.CloudFrontUrl = dcurepuzhyubr.cloudfront.net
Stack ARN:
arn:aws:cloudformation:us-east-1:123456789012:stack/cdk-basic-website-template/d92ffbc0-18d3-11ed-b23b-12285e0da875
✨ Total time: 613sAssuming deployment is successful then go to the CdkBasicWebsiteTemplate.CloudFrontUrl value (the one ending in cloudfront.net) from your version of the output in a browser - you should the see a message saying "Hello CDK World!"
Once you've run npm install once in the directory you won't need to again
To run TypeScript type-checking, linting, and formatting checks together:
$ npm run local-checksIf you need to deploy the same app multiple times with different names to the same AWS account you will need to override the default stack name. This is common, for example, for development accounts.
To do this set the STACK_NAME environment variable:
$ STACK_NAME=my-app-stack npm run deployOr you can create a .env file, and specify the stack name there (see the example file.)
Finally you can use a CDK context variable:
$ npm run deploy -- --context stackName=my-app-stackYou can specify other CDK parameters in the same way as the context variable, e.g. to use hotswap deployment you can run the following:
$ npm run deploy -- --hotswap
...
⚠️ The --hotswap flag deliberately introduces CloudFormation drift to speed up deployments
⚠️ It should only be used for development - never use it for your production Stacks!
CdkBarebonesTemplate (cdk-barebones-template): deploying...To teardown the stack either (a) delete the stack from the CloudFormation console or (b) run the following.
IMPORTANT - if you haven't made any changes to the project this will delete the default stack (cdk-basic-website-template) in your Account + Region.
$ npm run cdk-destroy
...
Are you sure you want to delete: CdkBasicWebsiteTemplate (y/n)? y
CdkBasicWebsiteTemplate (cdk-basic-website-template): destroying...
✅ CdkBasicWebsiteTemplate (cdk-basic-website-template): destroyedIf you want to teardown a stack with name that's not the default you can use the same environment variable, .env file, or context flag configurtion that deploy uses.
Once teardown is completed you'll need to manually delete the contents of the S3 bucket and the bucket itself.
For other commands see the Usage section of the barebones project README.
The most immediate thing you'll want to do next is deploy some actually interesting content. By default this project uploads everything from src/site to your site, so you can just change the contents of that directory.
Alternatively if your site has a build process you may want to run that first, and change the sources property under the BucketDeployment instance in app.ts to point to your build output folder.
Other next steps including custom domain names, setting up multiple environments, using Github Actions, and more, can be found in the larger CDK full website template project.
My style of using CDK is a little different from the default templates provided by AWS. For more details, and reasoning, see the Motivation section of the bare-bones project Readme.
All of the primary resources in this example are serverless - in other words they automatically scale according to actual load, and their costs are tied to this load. Your biggest cost will likely be CloudFront - see the CloudFront pricing page here.
Note that if you are deploying frequently - e.g. in a development environment - you'll likely want to turn off CloudFront cache invalidation for non-production environments.
See the comment for the distribution property in the BucketDeployment instance in app.ts
If you have questions related to this example please add a Github issue, or drop me a line at mike@symphonia.io . I'm also on Mastodon at http://hachyderm.io/@mikebroberts and BlueSky at https://bsky.app/profile/mikebroberts.com .
- Rename to cdk-basic-website-template
- Standardize with updates from my other template projects
- Switch to Node 24 from 22
- Switch to ES Modules / ESM
- Add ESLint + Prettier with
local-checks,lint,lint-fix, andformatscripts - Update
tsconfig.jsonto@tsconfig/node24, addmodule: preserve/moduleResolution: bundler - Support
STACK_NAMEenvironment variable (including from .env) as alternative to--context stackName= - Add "multipleContexts" directory, use for .env-file-loading-code and default stack name
- Update dependencies
- Switch to Node 22 from 16
- Update package-lock to use latest versions of specified dependencies
- Use TypeScript 5
- Inline
cdk-websitecustom construct into project, and include following changes:- Change to Origin Access Control from Origin Identity Control for CloudFront to S3 access
- Specify custom logger on
BucketDeploymentso that we can specify log retention - Remove specifying various S3 bucket properties which are now default
- Switched to tsx from ts-node in CDK configuration
- And so run tsc manually during deploy to perform pre-deploy typechecking
- Removed no-longer used "import 'source-map-support/register'" from CDK
- Move cdk.json to src/cdk directory. This is for a couple of reasons:
- One fewer file in project root, which I think is A Good Thing
- Makes it easier to have repos with multiple, separate, CDK apps
- Modify app.ts to point to new (relative) location of site content
- Move
outputandrequireApprovalCDK settings from package.json to cdk.json- I hadn't read the docs enough to know they could be in cdk.json. Oops. This way is cleaner
- Add package-lock.json - these are specific dependency versions I've tested with