Cloudformation template for provisioning everything needed for a HTTPS static site
This repository contains a Cloudformation template and a script for provisioning:
- AWS::S3::Bucket
- AWS::CertificateManager::Certificate
- AWS::CloudFront::Distribution
- AWS::Route53::HostedZone
- AWS::Route53::RecordSetGroup
- A handful of other resources
It can be used to provision a Cloudformation stack which serves a static site (or static assets) with a CDN and HTTPS.
For more details on what is this script is, you can read the blog post I wrote about it
./provision [options] <site-name>
- You'll need the aws-cli installed to run the provision script.
- You'll need access to the AWS console to validate the TLS certificate.
Note
If you let the script create a new Route53 Hosted Zone, you'll have to configure the name servers with your Registrar before the script will complete**
-
Run the provision script with the domain name you're using for your static site, and any desired options.
- For example, the domain name you're using is
example.com
, run./provision "example.com"
- See Options for more details
- For example, the domain name you're using is
-
Point Your DNS at the Route53 Name Servers
[!IMPORTANT] This is required for the TLS certificate validation. If your domain is not pointed to the AWS nameservers, the deployment will hang for over an hour while it tries to validate domain ownership before it eventually gives up.
- If you're creating a new Hosted Zone (i.e. you haven't passed in either the
--hosted-zone-id
or--hosted-zone-stack
flag), the Nameservers will be printed to the terminal before the main Cloudformation stack starts provisioning. - Tell your domain registrar to use these name servers for your URL (this is usually done from within your domain registrar's portal)
- If you're creating a new Hosted Zone (i.e. you haven't passed in either the
-
Wait for the script to finish (it might take a while).
For full usage instructions, run
./provision --help
By default, the script will create a new Route53 Hosted Zone for your domain. To use an existing hosted zone set up in Route53 (e.g. if there are other DNS records for the domain), either of the following flags can be used.
-
--hosted-zone-id
Specify the ID of an existing Route53 Hosted Zone for your domain.
A Cloudformation stack will be created that exports the hosted zone id so that it can be used in the main site stack.
./provision example.com --hosted-zone-id Z1234567890ABCDEFG
-
--hosted-zone-stack
Specify the name of an existing CloudFormation stack that exports the hosted Zone ID.
The stack must export an output named
HostedZoneId
which the main site stack will use../provision example.com --hosted-zone-stack my-dns-stack
[!Note] If
--hosted-zone-stack
is specified,--hosted-zone-id
will be ignored.
Tags can be added to the CloudFormation stack (which will automatically tag all supported resources in the stack) with the --tag
or --tags
flags:
./provision example.com --tag "Environment=production" --tag "Team=MyTeam"
./provision example.net --tags "Environment=production Team=MyTeam"
These flags can be combined and used multiple times, and all tags will be applied. e.g. the following are equivalent
./provision example.com --tag "Environment=production" --tags "Domain=web Product=web" -tag "Team=MyTeam"
./provision example.com --tags "Environment=production Domain=web Product=web Team=MyTeam"
When CloudFormation applies the tags, the last value for the key will be set, e.g.
./provision example.com --tags "Environment=test Environment=production"
will set the Environment
tag with the value production
By default, this stack includes URI canonicalization that will do the following redirects to improve SEO:
- Normalizes multiple consecutive slashes (
/path//to/page/
→/path/to/page/
) - these are invalid paths anyway and S3 can't serve them - Redirects extensionless paths to directory format (
/about
→/about/
) - directories will serve theindex.html
file from the directory - Redirects trailing
index.html
to the directory (/about/index.html
→/about/
)
To disable URI canonicalization if you need non-standard URI handling (e.g. if your site uses extensionless files):
./provision example.com --no-canonical-uris
By default, the following security headers will be added to responses
Strict-Transport-Security: max-age=31536000;
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Cross-Origin-Opener-Policy: same-origin-allow-popups
Content-Security-Policy: frame-ancestors 'self'
These headers add some security benefits while being minimally restrictive, and are unlikely to cause issues. In some cases, these headers might cause problems (e.g. if pages from your site are embedded in IFrames on another site, or in some SSO/Payment flows), so these headers can be disabled with:
./provision example.com --no-security-headers
The default content security policy is deliberately permissive so as not to cause issues, and really only prevents embedding pages in cross-origin IFrames. If you want something more restrictive, you can do so with a meta tag in your pages e.g.
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src 'self' https: data:;">
Alternatively, the CSP header for all pages can be set with --csp-policy <policy>
e.g.:
./provision example.com --csp-policy "default-src 'self'; img-src 'self' https: data:; frame-ancestors 'none'"