This static website is served from a CloudFront distribution with content stored in S3, and uses a Lambda@Edge function defined for rewriting URIs - why this is necessary and a guide on how to publish a static website using CloudFront, Lambda@Edge and S3 is outlined in this post.
Why serve content from S3 using CloudFront and Lambda@Edge vs. a S3 Website Enabled Bucket?
There are three main differences between serving content from S3 buckets with the S3 website feature enabled vs. CloudFront as discussed below.
S3 Website Enabled Bucket
Serving content from S3 buckets with the S3 website feature enabled provides common webserver functionality, such as default directory documents (i.e. automatically adding index.html
as needed to directory paths ending in /
) and HTTP permanent or temporary redirects (i.e. HTTP 301 and 302 response codes).
However, S3 websites do not support HTTPS and only support HTTP.
For example, an S3 Website enabled Bucket:
-
With S3 Website Index Document set to
index.html
www.jeremyvincent.com
=> Returns HTTP 200 response with content fromwww.jeremyvincent.com/index.html
www.jeremyvincent.com/pages
=> Returns HTTP 200 response with content fromwww.jeremyvincent.com/pages/index.html
-
Redirects as S3 Object Metadata (See Redirect requests for an object for more information)
/path1.html
withx-amz-website-redirect-location
object metadata set to/path3.html
=> Returns HTTP 301 response redirecting to/path3.html
/path2.html
withx-amz-website-redirect-location
object metadata set tohttp://www.example.com
=> Returns HTTP 301 response redirecting tohttp://www.example.com
-
HTTP Only (No HTTPS)
CloudFront serving content from an S3 Bucket
Using CloudFront to serve content from S3 buckets that aren’t S3 website enabled doesn’t provide common webserver functionality, such as default directory documents (i.e. automatically adding index.html
as needed to directory paths ending in /
) nor HTTP permanent or temporary redirects (i.e. HTTP 301 and 302 response codes).
With CloudFront it is easy to both enable HTTPS support for the website and also redirect HTTP to HTTPS requests.
For example, CloudFront serving an S3 Bucket:
-
With CloudFront Default Root Object set to
index.html
www.jeremyvincent.com
=> Returns HTTP 200 response with content fromwww.jeremyvincent.com/index.html
www.jeremyvincent.com/pages
=> Returns HTTP 404 error response forwww.jeremyvincent.com/pages
-
Redirects are Ignored in S3 Object Metadata
/path1.html
withx-amz-website-redirect-location
object metadata set to/path3.html
=> Returns HTTP 200 response with content from/path1.html
/path2.html
withx-amz-website-redirect-location
object metadata set tohttp://www.example.com
=> Returns HTTP 200 response with content from/path2.html
-
HTTPS Supported
CloudFront and Lambda@Edge serving content from an S3 Bucket
However, using some simple Lambda@Edge functions you can support both default directory documents and HTTP temporary or permanent redirects easily.
Setup
Follow the below steps to configure Route 53, ACM, S3, CloudFront and Lambda@Edge to publish a static website:
- Use Route 53 for DNS
- Use ACM to create a Public Digital Certificate
- Use a S3 Bucket to host Website Content
- Use CloudFront to serve Website Content
- Use Lambda@Edge to serve Default Directory Documents
Use Route 53 for DNS
You need to have a DNS domain name such as jeremyvincent.com
that will be used as the friendly name for your website.
-
To create a public hosted zone for your DNS domain name (i.e.
jeremyvincent.com
) perform one of the following procedures in Route 53:- To register a domain name for your website (if you don’t already own a DNS domain name), follow Registering a new domain from the Route 53 documentation. This will also create a new public hosted zone in your AWS account that will be used to serve DNS information about your website.
- Otherwise, create a new public hosted zone in your AWS account that will be used to serve DNS information about your website by following Creating a public hosted zone from the Route 53 documentation.
I followed Configuring white-label name servers from the Route 53 documentation to create my public hosted zone with vanity/white-label name servers for the
jeremyvincent.com
domain by:-
Run
aws route53 create-reusable-delegation-set --caller-reference 2020-09.05-jnv01 --region us-east-1
to create a reusable delegation set:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
jvincent$ aws route53 create-reusable-delegation-set --caller-reference 2020-09.05-jnv01 --region us-east-1 { "Location": "https://route53.amazonaws.com/2013-04-01/delegationset/N05176881T8BF9ESBR4KL", "DelegationSet": { "Id": "/delegationset/N05176881T8BF9ESBR4KL", "CallerReference": "2020-09.05-jnv01", "NameServers": [ "ns-1403.awsdns-47.org", "ns-785.awsdns-34.net", "ns-1721.awsdns-23.co.uk", "ns-88.awsdns-11.com" ] } } jvincent$
-
Run
aws route53 create-hosted-zone --name jeremyvincent.com. --caller-reference 2020-09.05-jnv01 --hosted-zone-config Comment="Public domain for jeremyvincent.com." --delegation-set-id "/delegationset/N05176881T8BF9ESBR4KL" --region us-east-1
to create the public hosted zone for thejeremyvincent.com
domain.
Use the DelegationSet Id from line 5 of the output of the aws route53 create-reusable-delegation-set command above as the value for the--delegation-set-id
parameter:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
jvincent$ aws route53 create-hosted-zone --name jeremyvincent.com. --caller-reference 2020-09.05-jnv01 --hosted-zone-config Comment="Public domain for jeremyvincent.com." --delegation-set-id "/delegationset/N04321422UY3VNN5ORHSF" --region us-east-1 { "Location": "https://route53.amazonaws.com/2013-04-01/hostedzone/Z08977741A5MAE6BBMBBV", "HostedZone": { "Id": "/hostedzone/Z08977741A5MAE6BBMBBV", "Name": "jeremyvincent.com.", "CallerReference": "2020-09.05-jnv01", "Config": { "Comment": "Public domain for jeremyvincent.com.", "PrivateZone": false }, "ResourceRecordSetCount": 2 }, "ChangeInfo": { "Id": "/change/C097078738U5D1YWZBRN8", "Status": "PENDING", "SubmittedAt": "2020-09-05T05:47:57.268000+00:00" }, "DelegationSet": { "Id": "/delegationset/N05176881T8BF9ESBR4KL", "CallerReference": "2020-09.05-jnv01", "NameServers": [ "ns-1403.awsdns-47.org", "ns-785.awsdns-34.net", "ns-1721.awsdns-23.co.uk", "ns-88.awsdns-11.com" ] } } jvincent$
-
Run
dig A ns-1403.awsdns-47.org +short
anddig AAAA ns-1403.awsdns-47.org +short
for each of the four listed name servers to determine the IPv4 and IPv6 addresses that will be used to create theA
andAAAA
Host records for your white-label name servers:jvincent$ dig A ns-1403.awsdns-47.org +short 205.251.197.123 jvincent$ dig AAAA ns-1403.awsdns-47.org +short 2600:9000:5305:7b00::1 jvincent$ dig A ns-1721.awsdns-23.co.uk +short 205.251.198.185 jvincent$ dig AAAA ns-1721.awsdns-23.co.uk +short 2600:9000:5306:b900::1 jvincent$ dig A ns-785.awsdns-34.net +short 205.251.195.17 jvincent$ dig AAAA ns-785.awsdns-34.net +short 2600:9000:5303:1100::1 jvincent$ dig A ns-88.awsdns-11.com +short 205.251.192.88 jvincent$ dig AAAA ns-88.awsdns-11.com +short 2600:9000:5300:5800::1 jvincent$
-
Create records for your white-label name servers by creating a Route 53 ChangeRecordSet file named
new-ns-jeremyvincent.com.txt
with theA
andAAAA
Host records of the four name servers.
The ChangeRecordSet file forjeremyvincent.com
is here.
See How do I create a simple resource record set in Amazon Route 53 using the AWS CLI? for more information. -
Run
aws route53 change-resource-record-sets --hosted-zone-id Z08977741A5MAE6BBMBBV --change-batch file://new-ns-jeremyvincent.com.txt
to create your white-label name server records in the newly created public hosted zone.
Use the HostedZone Id from line 5 of the output of the aws route53 create-hosted-zone command above as the value for the--hosted-zone-id
parameter:1 2 3 4 5 6 7 8 9 10 11
jvincent$ aws route53 change-resource-record-sets --hosted-zone-id Z08977741A5MAE6BBMBBV --change-batch file://new-ns-jeremyvincent.com.txt { "ChangeInfo": { "Id": "/change/C013962521VQMHQB35WY6", "Status": "PENDING", "SubmittedAt": "2020-09-05T08:14:18.929000+00:00", "Comment": "Create R53 DNS records for vanity name servers for the jeremyvincent.com. domain in the jeremyvincent-prod account" } } jvincent$
-
Run
aws route53 get-change --id change/C013962521VQMHQB35WY6
to check the status of your name server record changes.
Use the ChangeInfo Id from line 4 of the output of the aws route53 change-resource-record-sets command above as the value for the--id
parameter:1 2 3 4 5 6 7 8 9 10 11
jvincent$ aws route53 get-change --id change/C013962521VQMHQB35WY6 { "ChangeInfo": { "Id": "/change/C013962521VQMHQB35WY6", "Status": "INSYNC", "SubmittedAt": "2020-09-05T08:14:18.929000+00:00", "Comment": "Create R53 DNS records for vanity name servers for the jeremyvincent.com. domain in the jeremyvincent-prod account" } } jvincent$
The DNS changes are propagated to all Route 53 DNS servers when the Status on line 5 from aws route53 get-change above returns
INSYNC
(typically this occurs within 60 seconds) and you can continue to the next step.
If the Status showsPENDING
then the changes are still propagating and you should wait before continuing. -
Update NS and SOA records for the
jeremyvincent.com
domain.-
Update the SOA record by replacing the name of the Route 53 name server with the name of one of your white-label name servers.
Replace
ns-1403.awsdns-47.org. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400
with the name of one of your white-label name servers:ns1.jeremyvincent.com. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400
. -
Update the NS records by replacing the names of the current Route 53 name servers with the names of your four white-label name servers:
ns-1403.awsdns-47.org.
ns-1721.awsdns-23.co.uk.
ns-785.awsdns-34.net.
ns-88.awsdns-11.com.
with
ns1.jeremyvincent.com.
ns2.jeremyvincent.com.
ns3.jeremyvincent.com.
ns4.jeremyvincent.com.
-
-
Create glue records and change the registrar’s name servers for the
jeremyvincent.com
domain by following Adding or changing name servers and glue records for a domain. -
Validate your DNS changes by running
dig SOA jeremyvincent.com. +short
anddig NS jeremyvincent.com. +short
to check the SOA and NS DNS records for thejeremyvincent.com
domain:jvincent$ dig SOA jeremyvincent.com. +short ns1.jeremyvincent.com. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400 jvincent$ dig NS jeremyvincent.com. +short ns1.jeremyvincent.com. ns2.jeremyvincent.com. ns3.jeremyvincent.com. ns4.jeremyvincent.com. jvincent$
-
This section is complete once the newly created public hosted zone responds with the correct SOA and NS DNS records for the
jeremyvincent.com
domain.
You will NOT be able to create your public digital certificate using AWS Certificate Manager (ACM) in the section below or configure your CloudFront Distribution in the later Use CloudFront to serve Website Content section until your public hosted zone has been successfully created.
Use ACM to create a Public Digital Certificate
To enable HTTPS for your website, you will need to have a public digital certificate issued for your domain name such as jeremyvincent.com
.
AWS Certificate Manager (ACM) makes it easy to provision, manage, deploy, and renew SSL/TLS certificates on the AWS platform. See the ACM User Guide for more information.
Since ACM provides public digital certificates at no additional cost and integrates nicely with CloudFront, we will use it to provide our public digital certificate.
Follow the steps below to request a new public digital ceritificate from ACM:
-
Run
aws acm request-certificate --region us-east-1 --domain-name jeremyvincent.com --subject-alternative-names "www.jeremyvincent.com" "cdn.jeremyvincent.com" --options CertificateTransparencyLoggingPreference=ENABLED --validation-method DNS --idempotency-token 20201123jnv01 --tags Key="jv:purpose",Value="For the jeremyvincent.com CloudFront Web Distribution." Key="jv:owner",Value="jeremy.vincent@gmail.com" Key="jv:environment",Value="production" Key="Name",Value="jeremyvincent.com"
to request a new public digital certificate for thejeremyvincent.com
domain name, along with thewww.jeremyvincent.com
andcdn.jeremyvincent.com
alternative domain names:IMPORTANT NOTE: We need to explicitly create our new public digital certificate with ACM in the
us-east-1
region for it to be available for use by CloudFront; this is easily performed by including the--region us-east-1
AWS CLI option.1 2 3 4 5 6
jvincent$ aws acm request-certificate --region us-east-1 --domain-name jeremyvincent.com --subject-alternative-names "www.jeremyvincent.com" "cdn.jeremyvincent.com" --options CertificateTransparencyLoggingPreference=ENABLED --validation-method DNS --idempotency-token 20201123jnv01 --tags Key="jv:purpose",Value="For the jeremyvincent.com CloudFront Web Distribution." Key="jv:owner",Value="jeremy.vincent@gmail.com" Key="jv:environment",Value="production" Key="Name",Value="jeremyvincent.com" { "CertificateArn": "arn:aws:acm:us-east-1:418110697901:certificate/e09ad0d1-9890-4df2-a63d-0007c18604e0" } jvincent$
-
Run
aws acm describe-certificate --region us-east-1 --certificate-arn "arn:aws:acm:us-east-1:418110697901:certificate/e09ad0d1-9890-4df2-a63d-0007c18604e0" --no-paginate
to view the details for the requestedjeremyvincent.com
ACM public digitial certificate.
Use the CertificateArn from line 3 of the output of the aws acm request-certificate command above as the value for the--certificate-arn
parameter:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
jvincent$ aws acm describe-certificate --region us-east-1 --certificate-arn arn:aws:acm:us-east-1:418110697901:certificate/e09ad0d1-9890-4df2-a63d-0007c18604e0 --no-paginate { "Certificate": { "CertificateArn": "arn:aws:acm:us-east-1:418110697901:certificate/e09ad0d1-9890-4df2-a63d-0007c18604e0", "DomainName": "jeremyvincent.com", "SubjectAlternativeNames": [ "jeremyvincent.com", "www.jeremyvincent.com", "cdn.jeremyvincent.com" ], "DomainValidationOptions": [ { "DomainName": "jeremyvincent.com", "ValidationDomain": "jeremyvincent.com", "ValidationStatus": "PENDING_VALIDATION", "ResourceRecord": { "Name": "_c19dc234dd7c683e397588055c4bd9ea.jeremyvincent.com.", "Type": "CNAME", "Value": "_2f786c052ea0a4dede819fa161e05aa7.wggjkglgrm.acm-validations.aws." }, "ValidationMethod": "DNS" }, { "DomainName": "www.jeremyvincent.com", "ValidationDomain": "www.jeremyvincent.com", "ValidationStatus": "PENDING_VALIDATION", "ResourceRecord": { "Name": "_83b2766e4e1a3d0ef0181a156dff84df.www.jeremyvincent.com.", "Type": "CNAME", "Value": "_319acc2836901aba6a358be7ae62011c.wggjkglgrm.acm-validations.aws." }, "ValidationMethod": "DNS" }, { "DomainName": "cdn.jeremyvincent.com", "ValidationDomain": "cdn.jeremyvincent.com", "ValidationStatus": "PENDING_VALIDATION", "ResourceRecord": { "Name": "_9cf814487b9f5addf7b80063c9ca1706.cdn.jeremyvincent.com.", "Type": "CNAME", "Value": "_30fb7b4e608a32c699e2d32a4d0ab8f9.wggjkglgrm.acm-validations.aws." }, "ValidationMethod": "DNS" } ], "Subject": "CN=jeremyvincent.com", "Issuer": "Amazon", "CreatedAt": "2020-11-23T16:57:34+13:00", "Status": "PENDING_VALIDATION", "KeyAlgorithm": "RSA-2048", "SignatureAlgorithm": "SHA256WITHRSA", "InUseBy": [], "Type": "AMAZON_ISSUED", "KeyUsages": [], "ExtendedKeyUsages": [], "RenewalEligibility": "INELIGIBLE", "Options": { "CertificateTransparencyLoggingPreference": "ENABLED" } } } jvincent$
NOTE: The status of the new ACM certificate is listed as"Status": "PENDING_VALIDATION"
on line 49 since we haven’t created the required DNS records in Route 53 for ACM to validate that we own the domain names listed in the public certificate request.
Requests for ACM certificates time out if they are not validated within 72 hours. -
Create the required DNS validation records for your ACM public certificate by creating a Route 53 ChangeRecordSet file named
new-acm-jeremyvincent.com.txt
withCNAME
records for each of the threeResourceRecord
’s (see lines 16, 27 and 38 above) listed under theDomainValidationOptions
section on line 11 above.
The ChangeRecordSet file to enable DNS validation of thejeremyvincent.com
ACM public certificate is here.
See How do I create a simple resource record set in Amazon Route 53 using the AWS CLI? for more information. -
Run
aws route53 change-resource-record-sets --hosted-zone-id Z08977741A5MAE6BBMBBV --change-batch file://new-acm-jeremyvincent.com.txt
to create your DNS validation records for ACM in the public hosted zone that was newly created in the Use Route 53 for DNS section above.
Use the HostedZone Id from line 5 of the output of the aws route53 create-hosted-zone command used earlier as the value for the--hosted-zone-id
parameter:1 2 3 4 5 6 7 8 9 10 11
jvincent$ aws route53 change-resource-record-sets --hosted-zone-id Z08977741A5MAE6BBMBBV --change-batch file://new-acm-jeremyvincent.com.txt { "ChangeInfo": { "Id": "/change/C00843573TC2ZSVHM391J", "Status": "PENDING", "SubmittedAt": "2020-11-23T04:05:56.159000+00:00", "Comment": "Create R53 DNS records for DNS validation of the jeremyvincent.com ACM certificate for the jeremyvincent.com. domain in the jeremyvincent-prod account" } } jvincent$
-
Run
aws route53 get-change --id "/change/C00843573TC2ZSVHM391J"
to view the status of our changes to thejeremyvincent.com
domain.
Use the ChangeInfo Id from line 4 of the output of the aws route53 change-resource-record-sets command above as the value for the--id
parameter:When aws route53 get-change returns a status of1 2 3 4 5 6 7 8 9 10 11
jvincent$ aws route53 get-change --id "/change/C00843573TC2ZSVHM391J" { "ChangeInfo": { "Id": "/change/C00843573TC2ZSVHM391J", "Status": "INSYNC", "SubmittedAt": "2020-11-23T04:05:56.159000+00:00", "Comment": "Create R53 DNS records for DNS validation of the jeremyvincent.com ACM certificate for the jeremyvincent.com. domain in the jeremyvincent-prod account" } } jvincent$
INSYNC
then we know the changes have propagated to all Route 53 name servers and we can continue (typically this occurs within 60 seconds). -
Run
aws acm describe-certificate --region us-east-1 --certificate-arn "arn:aws:acm:us-east-1:418110697901:certificate/e09ad0d1-9890-4df2-a63d-0007c18604e0" --no-paginate
to view the details for the requestedjeremyvincent.com
ACM public digital certificate.
Use the CertificateArn from line 3 of the output of the aws acm request-certificate command above as the value for the--certificate-arn
parameter:When the Status on line 51 from aws acm describe-certificate above returns1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
jvincent$ aws acm describe-certificate --region us-east-1 --certificate-arn arn:aws:acm:us-east-1:418110697901:certificate/e09ad0d1-9890-4df2-a63d-0007c18604e0 --no-paginate { "Certificate": { "CertificateArn": "arn:aws:acm:us-east-1:418110697901:certificate/e09ad0d1-9890-4df2-a63d-0007c18604e0", "DomainName": "jeremyvincent.com", "SubjectAlternativeNames": [ "jeremyvincent.com", "www.jeremyvincent.com", "cdn.jeremyvincent.com" ], "DomainValidationOptions": [ { "DomainName": "jeremyvincent.com", "ValidationDomain": "jeremyvincent.com", "ValidationStatus": "SUCCESS", "ResourceRecord": { "Name": "_c19dc234dd7c683e397588055c4bd9ea.jeremyvincent.com.", "Type": "CNAME", "Value": "_2f786c052ea0a4dede819fa161e05aa7.wggjkglgrm.acm-validations.aws." }, "ValidationMethod": "DNS" }, { "DomainName": "www.jeremyvincent.com", "ValidationDomain": "www.jeremyvincent.com", "ValidationStatus": "SUCCESS", "ResourceRecord": { "Name": "_83b2766e4e1a3d0ef0181a156dff84df.www.jeremyvincent.com.", "Type": "CNAME", "Value": "_319acc2836901aba6a358be7ae62011c.wggjkglgrm.acm-validations.aws." }, "ValidationMethod": "DNS" }, { "DomainName": "cdn.jeremyvincent.com", "ValidationDomain": "cdn.jeremyvincent.com", "ValidationStatus": "SUCCESS", "ResourceRecord": { "Name": "_9cf814487b9f5addf7b80063c9ca1706.cdn.jeremyvincent.com.", "Type": "CNAME", "Value": "_30fb7b4e608a32c699e2d32a4d0ab8f9.wggjkglgrm.acm-validations.aws." }, "ValidationMethod": "DNS" } ], "Serial": "0b:35:1c:38:b9:0a:e7:24:21:1f:35:b7:84:7a:eb:5b", "Subject": "CN=jeremyvincent.com", "Issuer": "Amazon", "CreatedAt": "2020-11-23T16:57:34+13:00", "IssuedAt": "2020-11-23T17:06:27+13:00", "Status": "ISSUED", "NotBefore": "2020-11-23T13:00:00+13:00", "NotAfter": "2021-12-23T12:59:59+13:00", "KeyAlgorithm": "RSA-2048", "SignatureAlgorithm": "SHA256WITHRSA", "InUseBy": [], "Type": "AMAZON_ISSUED", "KeyUsages": [ { "Name": "DIGITAL_SIGNATURE" }, { "Name": "KEY_ENCIPHERMENT" } ], "ExtendedKeyUsages": [ { "Name": "TLS_WEB_SERVER_AUTHENTICATION", "OID": "1.3.6.1.5.5.7.3.1" }, { "Name": "TLS_WEB_CLIENT_AUTHENTICATION", "OID": "1.3.6.1.5.5.7.3.2" } ], "RenewalEligibility": "INELIGIBLE", "Options": { "CertificateTransparencyLoggingPreference": "ENABLED" } } } jvincent$
ISSUED
then we know the requested ACM certificate has been succesfully validated and issued.NOTE: DNS validation of new ACM certificates typically occurs very quickly. However, some DNS providers can take 24–48 hours to propagate DNS records. ACM periodically checks for the required DNS validation record(s). This process can’t be manually checked.
See Why is my AWS Certificate Manager (ACM) certificate DNS validation status still pending validation? for troubleshooting pending validation of your ACM certificate. -
This section is complete once the newly created ACM certificate has been succesfully issued.
You will NOT be able to configure your CloudFront Distribution in the later Use CloudFront to serve Website Content section using this ACM certificate until it has been successfully issued.
Use a S3 Bucket to host Website Content
<To Do>
Document S3 Bucket creation and permissions.
See the below links for more information:
Use CloudFront to serve Website Content
<To Do>
Document CloudFront Distribution creation and permissions (including OAI).
See the below links for more information:
- Creating, updating, and deleting distributions
- Restricting access to Amazon S3 content by using an origin access identity (OAI)
Use Lambda@Edge to serve Default Directory Documents
<To Do>
Document Lambda@Edge function and CloudFront configuration.
See the below links for more information:
Updates
Follow the below steps to publish updates to your static website hosted on S3 and CloudFront:
Generate Static Site Content using Hugo
Run the following commands from the macOS terminal to generate the updated static website content in the public/
subdirectory of the jvincent-website
directory:
NOTE: Only content marked non-draft will be generated.
|
|
Publish to S3
This will synchronise the contents of your local public/
directory with the main/
folder in the jeremyvincent-production-us-east-1-jvincent-website
S3 bucket.
-
Run
aws s3 sync public/ s3://jeremyvincent-production-us-east-1-jvincent-website/main/ --size-only --exclude ".DS_Store" --exclude "*/.DS_Store" --delete --acl bucket-owner-full-control --dryrun
to perform a “dry-run”; this verifies what changes would have been made to the S3 bucket but does NOT make any changes:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
jvincent$ aws s3 sync public/ s3://jeremyvincent-production-us-east-1-jvincent-website/main/ --size-only --exclude ".DS_Store" --exclude "*/.DS_Store" --delete --acl bucket-owner-full-control --dryrun (dryrun) upload: public/2020/04/my-first-post/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/2020/04/my-first-post/index.html (dryrun) upload: public/2020/09/publishing-a-static-site-with-aws/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/2020/09/publishing-a-static-site-with-aws/index.html (dryrun) upload: public/404.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/404.html (dryrun) delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/aws/index.html (dryrun) delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/aws/index.xml (dryrun) delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/aws/page/1/index.html (dryrun) delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/hugo/index.html (dryrun) delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/hugo/index.xml (dryrun) delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/hugo/page/1/index.html (dryrun) upload: public/categories/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/index.html (dryrun) upload: public/categories/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/index.xml (dryrun) delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/website/index.html (dryrun) delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/website/index.xml (dryrun) delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/website/page/1/index.html (dryrun) upload: public/favicon.ico to s3://jeremyvincent-production-us-east-1-jvincent-website/main/favicon.ico (dryrun) upload: public/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/index.html (dryrun) upload: public/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/index.xml (dryrun) upload: public/pages/about/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/pages/about/index.html (dryrun) upload: public/pages/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/pages/index.html (dryrun) upload: public/pages/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/pages/index.xml (dryrun) upload: public/posts/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/posts/index.html (dryrun) upload: public/posts/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/posts/index.xml (dryrun) upload: public/resources/publishing-a-static-site-with-aws/new-acm-jeremyvincent.com.txt to s3://jeremyvincent-production-us-east-1-jvincent-website/main/resources/publishing-a-static-site-with-aws/new-acm-jeremyvincent.com.txt (dryrun) upload: public/sitemap.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/sitemap.xml (dryrun) delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/syntax.css (dryrun) upload: public/tags/aws/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/aws/index.html (dryrun) upload: public/tags/aws/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/aws/index.xml (dryrun) upload: public/tags/aws/page/1/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/aws/page/1/index.html (dryrun) upload: public/tags/hugo/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/hugo/index.html (dryrun) upload: public/tags/hugo/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/hugo/index.xml (dryrun) upload: public/tags/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/index.html (dryrun) upload: public/tags/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/index.xml (dryrun) upload: public/tags/website/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/website/index.html (dryrun) upload: public/tags/website/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/website/index.xml jvincent$
Review the changes that would have been made had the aws s3 sync command not been running in dry-run mode, to ensure they match with changes you have made to the website content.
-
Run
aws s3 sync public/ s3://jeremyvincent-production-us-east-1-jvincent-website/main/ --size-only --exclude ".DS_Store" --exclude "*/.DS_Store" --delete --acl bucket-owner-full-control
to perform the LIVE update to the S3 bucket:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
jvincent$ aws s3 sync public/ s3://jeremyvincent-production-us-east-1-jvincent-website/main/ --size-only --exclude ".DS_Store" --exclude "*/.DS_Store" --delete --acl bucket-owner-full-control delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/hugo/index.xml delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/aws/page/1/index.html delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/aws/index.html delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/aws/index.xml delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/hugo/page/1/index.html upload: public/2020/04/my-first-post/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/2020/04/my-first-post/index.html delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/hugo/index.html delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/website/index.html delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/website/page/1/index.html delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/website/index.xml upload: public/404.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/404.html upload: public/categories/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/index.xml upload: public/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/index.xml upload: public/categories/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/categories/index.html upload: public/favicon.ico to s3://jeremyvincent-production-us-east-1-jvincent-website/main/favicon.ico upload: public/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/index.html upload: public/pages/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/pages/index.xml upload: public/posts/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/posts/index.xml upload: public/pages/about/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/pages/about/index.html delete: s3://jeremyvincent-production-us-east-1-jvincent-website/main/syntax.css upload: public/pages/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/pages/index.html upload: public/resources/publishing-a-static-site-with-aws/new-acm-jeremyvincent.com.txt to s3://jeremyvincent-production-us-east-1-jvincent-website/main/resources/publishing-a-static-site-with-aws/new-acm-jeremyvincent.com.txt upload: public/sitemap.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/sitemap.xml upload: public/posts/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/posts/index.html upload: public/tags/aws/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/aws/index.html upload: public/tags/aws/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/aws/index.xml upload: public/tags/aws/page/1/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/aws/page/1/index.html upload: public/tags/hugo/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/hugo/index.xml upload: public/tags/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/index.xml upload: public/tags/website/index.xml to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/website/index.xml upload: public/tags/hugo/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/hugo/index.html upload: public/tags/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/index.html upload: public/tags/website/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/tags/website/index.html upload: public/2020/09/publishing-a-static-site-with-aws/index.html to s3://jeremyvincent-production-us-east-1-jvincent-website/main/2020/09/publishing-a-static-site-with-aws/index.html jvincent$
Update CloudFront Distribution
NOTE: This is only required if you have configured your site content to be cached for a long period of time; either directly by setting cache-control headers on the uploaded S3 objects or via CloudFront caching behaviours.
This will invalidate the root page and various page listings on your CloudFront Distribution serving your static website. See Invalidating files for more information.
-
Run
aws cloudfront create-invalidation --distribution-id E8XLUV8WNKTOL --paths /index.html / "/pages/*" "/posts/*" "/tags/*" "/categories/*" "/2020/*"
to invalidate the root page and page listings on your CloudFront Distribution:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
jvincent$ aws cloudfront create-invalidation --distribution-id E8XLUV8WNKTOL --paths /index.html / "/pages/*" "/posts/*" "/tags/*" "/categories/*" "/2020/*" { "Location": "https://cloudfront.amazonaws.com/2020-05-31/distribution/E8XLUV8WNKTOL/invalidation/I6MYTHAWI0YXG", "Invalidation": { "Id": "I6MYTHAWI0YXG", "Status": "InProgress", "CreateTime": "2022-03-05T04:10:53.809000+00:00", "InvalidationBatch": { "Paths": { "Quantity": 6, "Items": [ "/pages/*", "/2020/*", "/posts/*", "/index.html", "/tags/*", "/" ] }, "CallerReference": "cli-1646453450-342376" } } } jvincent$
Once the invalidation is complete, all your updated website content will now be served via CloudFront.
NOTE: Web browsers may still show outdated site content due to Web browsers and/or corporate proxies caching your content at the time when the content was previously requested. This caching will be based on your object and/or CloudFront cache settings at the time of the request.