<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Erik Petrinec</title>
    <description>The latest articles on Forem by Erik Petrinec (@htmnk).</description>
    <link>https://forem.com/htmnk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F989502%2Fa07a597a-2896-4f8c-8f57-dd4c0af2a536.jpeg</url>
      <title>Forem: Erik Petrinec</title>
      <link>https://forem.com/htmnk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/htmnk"/>
    <language>en</language>
    <item>
      <title>Caching Site Assets with AWS CDK &amp; S3 - snippet</title>
      <dc:creator>Erik Petrinec</dc:creator>
      <pubDate>Wed, 01 Feb 2023 19:30:50 +0000</pubDate>
      <link>https://forem.com/htmnk/caching-site-assets-with-aws-cdk-s3-snippet-3hp9</link>
      <guid>https://forem.com/htmnk/caching-site-assets-with-aws-cdk-s3-snippet-3hp9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Getting caching right yields huge performance benefits, saves bandwidth and reduces server costs... &lt;a href="https://jakearchibald.com/2016/caching-best-practices/" rel="noopener noreferrer"&gt;more&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;s3deploy&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-s3-deployment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CacheControlMaxAge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;31536000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;SiteBucketDeploymentProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;s3deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BucketDeploymentProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sources&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;sitePaths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="na"&gt;longCacheFileExtensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CachedBucketDeploymentProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SiteBucketDeploymentProps&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CacheControlMaxAge&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CachedBucketDeployment&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;s3deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BucketDeployment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CachedBucketDeploymentProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sitePaths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;longCacheFileExtensions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxAge&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;longCacheFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`.{&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;longCacheFileExtensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;}`&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assetOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;maxAge&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;31536000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
          &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/*.*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!**/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;longCacheFiles&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
          &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;longCacheFiles&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cacheControl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`max-age=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,public,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;
      &lt;span class="nx"&gt;maxAge&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;31536000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;immutable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;must-revalidate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;

    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s3deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;sitePaths&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;assetOptions&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
      &lt;span class="na"&gt;cacheControl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s3deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CacheControl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheControl&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
      &lt;span class="na"&gt;prune&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cm"&gt;/**
 * Creates multiple bucket deployments for the same destination bucket
 * in order to set different Cache-Control headers (rule of thumb:
 * either a very long `maxAge` or `maxAge: 0`) for different types of files.
 *
 * `.css`, `.js` files are usually distributed with a hash such as `[name].[contenthash].js`
 * so a `max-age=31536000,public,immutable` Cache-Control header will be set.
 *
 * `.html` files are usually expected to be invalidated every time since their URLs
 * cannot be versioned and their content must be able to change so a
 * `public,max-age=0,must-revalidate` header will be set for all the file extensions
 * that are not found inside `longCacheFileExtensions`.
 *
 * @example
 * new SiteBucketDeployment(this, 'SiteDeployment', {
 *   longCacheFileExtensions: ['js', 'css'],
 *   sitePaths: ['..', '..', 'dist'],
 * })
 *
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SiteBucketDeployment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;_maxAgePatterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CacheControlMaxAge&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;31536000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;_constructIdSuffix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CacheControlMaxAge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NoCache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;31536000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LongCache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;_createCachedBucketDeployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CachedBucketDeploymentProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;cleanupDeployment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s3deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BucketDeployment&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CachedBucketDeployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_constructIdSuffix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addDependency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cleanupDeployment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SiteBucketDeploymentProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * Initial deployment for cleaning up the content from previous deploys.
     * Since `prune: false` is used for the actual deployments, in some cases the
     * unnecessary files (i.e some frameworks generate different static/&amp;lt;hash&amp;gt; directories
     * on every new build, etc.) won't be deleted otherwise.
     *
     * @see https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3_deployment-readme.html#prune
     */&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cleanupDeployment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;s3deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketDeployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cleanup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s3deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sitePaths&lt;/span&gt;&lt;span class="p"&gt;))],&lt;/span&gt;
      &lt;span class="na"&gt;prune&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_maxAgePatterns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_createCachedBucketDeployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxAge&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;cleanupDeployment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://gist.github.com/htmnk/507ece0ecf98d1dd0cb4aa94f53a291b#file-sitebucketdeployment-ts" rel="noopener noreferrer"&gt;View Gist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When using AWS CDK we can set the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control" rel="noopener noreferrer"&gt;Cache-Control&lt;/a&gt; headers with &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingMetadata.html" rel="noopener noreferrer"&gt;S3 object metadata&lt;/a&gt; on our &lt;code&gt;BucketDeployment&lt;/code&gt; construct (check out my article on how to deploy a site to AWS with CDK below if you need an introduction to CDK).&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/htmnk" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F989502%2Fa07a597a-2896-4f8c-8f57-dd4c0af2a536.jpeg" alt="htmnk"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/htmnk/deploy-a-static-site-to-aws-s3-and-cloudfront-using-aws-cdk-21d4" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Deploy a static site to AWS S3 and CloudFront using AWS CDK&lt;/h2&gt;
      &lt;h3&gt;Erik Petrinec ・ Jan 26 '23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#aws&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#typescript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tutorial&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule of thumb&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set the &lt;code&gt;max-age=31536000,public,immutable&lt;/code&gt; directives for all assets that have been processed with &lt;code&gt;[contenthash]&lt;/code&gt; substitutions. Many frameworks typically produce files with names such as &lt;code&gt;[name].[contenthash].js&lt;/code&gt;, etc. when building the app (see &lt;a href="https://webpack.js.org/guides/caching/" rel="noopener noreferrer"&gt;Webpack Caching&lt;/a&gt;). These are usually all the &lt;code&gt;.js&lt;/code&gt;, &lt;code&gt;.css&lt;/code&gt;, and statically imported images. These directives can also be used for other files, but we need to ensure that we version the path to the files ourselves by changing the file name to revalidate. Otherwise, our users may have stale files loaded until they perform a hard refresh.&lt;/li&gt;
&lt;li&gt;Alternatively, set &lt;code&gt;public,max-age=0,must-revalidate&lt;/code&gt; (or &lt;code&gt;max-age=0,no-cache,no-store&lt;/code&gt; - see &lt;a href="https://stackoverflow.com/questions/18148884/difference-between-no-cache-and-must-revalidate-for-cache-control" rel="noopener noreferrer"&gt;difference&lt;/a&gt;) directives. These are typically all the document &lt;code&gt;.html&lt;/code&gt; files. Since these URLs cannot be versioned and the content changes frequently, we don't want to cache them at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Custom Props&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sitePaths&lt;/code&gt; - The site build folder that is being deployed. Uses &lt;a href="https://nodejs.org/api/path.html#pathjoinpaths" rel="noopener noreferrer"&gt;path.join&lt;/a&gt; to join and normalize the resulting path.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;longCacheFileExtensions&lt;/code&gt; - Sets &lt;code&gt;max-age=31536000&lt;/code&gt; to all the files with the specified extension located inside the &lt;code&gt;sitePaths&lt;/code&gt; directory. Otherwise, it sets &lt;code&gt;max-age=0&lt;/code&gt; to all the files that are not found inside the array.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can play around with the &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3_deployment-readme.html#exclude-and-include-filters" rel="noopener noreferrer"&gt;asset bundling exclude filters&lt;/a&gt; and exclude/include directories or files instead of file extensions as I did above. The &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3_deployment-readme.html#aws-s3-deployment-construct-library" rel="noopener noreferrer"&gt;s3deploy.BucketDeployment&lt;/a&gt; can be then replaced with the &lt;code&gt;SiteBucketDeployment&lt;/code&gt; construct&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SiteBucketDeployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BlogBucketDeployment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;longCacheFileExtensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;// relative to where the `SiteBucketDeployment.ts` file is located&lt;/span&gt;
  &lt;span class="na"&gt;sitePaths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;destinationBucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;distributionPaths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://gist.github.com/htmnk/507ece0ecf98d1dd0cb4aa94f53a291b#file-cdk-stack-ts" rel="noopener noreferrer"&gt;View Gist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💡 The same results could also be achieved through the AWS Console manually if you are not using CDK, see &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/add-object-metadata.html" rel="noopener noreferrer"&gt;editing object metadata in the Amazon S3 console&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Open the Amazon S3 console and your bucket.&lt;/li&gt;
&lt;li&gt; Select the check box to the left of a file/directory.&lt;/li&gt;
&lt;li&gt; On the Actions menu, choose Edit actions, and choose Edit metadata.&lt;/li&gt;
&lt;li&gt; Choose Add metadata.&lt;/li&gt;
&lt;li&gt; For metadata Type, select System-defined.&lt;/li&gt;
&lt;li&gt; Select &lt;code&gt;Cache-Control&lt;/code&gt; for the key and add &lt;code&gt;max-age=31536000,public,immutable&lt;/code&gt; as the value.&lt;/li&gt;
&lt;li&gt; When you are done, hit Save Changes and Amazon S3 should edit all the selected files recursively, you can verify it by opening a specific file and scrolling down to the metadata section.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>aws</category>
      <category>performance</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Deploy a static site to AWS S3 and CloudFront using AWS CDK</title>
      <dc:creator>Erik Petrinec</dc:creator>
      <pubDate>Thu, 26 Jan 2023 09:44:44 +0000</pubDate>
      <link>https://forem.com/htmnk/deploy-a-static-site-to-aws-s3-and-cloudfront-using-aws-cdk-21d4</link>
      <guid>https://forem.com/htmnk/deploy-a-static-site-to-aws-s3-and-cloudfront-using-aws-cdk-21d4</guid>
      <description>&lt;p&gt;Amazon Web Services (AWS) is the most comprehensive and widely adopted cloud platform in the world. Using the AWS Cloud Development Kit (CDK), we can easily define our cloud infrastructure as code (using TypeScript) and host our site on S3, serving it with Amazon's high-performance CloudFront CDN.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full Source Code available on GitHub&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/htmnk" rel="noopener noreferrer"&gt;
        htmnk
      &lt;/a&gt; / &lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter" rel="noopener noreferrer"&gt;
        aws-cdk-static-site-starter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An AWS CDK, S3 &amp;amp; CloudFront static site deploy starter template (blog post repo) 
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Static Site Deploy AWS CDK template&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;🎉 Tutorial &lt;a href="https://e53nec.com/posts/aws-cdk-s3-cloudfront-static-site-deploy/" rel="nofollow noopener noreferrer"&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is a starter template for deploying a static site with AWS CDK. Make sure to add your own &lt;code&gt;.env&lt;/code&gt; file to &lt;code&gt;/cdk&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;CDK_REGION=us-east-1
CDK_ACCOUNT=2383838383
DOMAIN_NAME=e53nec.com&lt;/pre&gt;

&lt;/div&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/htmnk/aws-cdk-static-site-starter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;☑ Node.js (LTS recommended), Git and TypeScript (globally via npm) installed&lt;/li&gt;
&lt;li&gt;☑ Have an AWS account and access to the &lt;a href="https://aws.amazon.com/console/" rel="noopener noreferrer"&gt;AWS Management Console&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Todo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;☐ &lt;strong&gt;Create a demo static site&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;build a site to a ready-to-deploy folder, I will be using &lt;a href="https://gohugo.io/" rel="noopener noreferrer"&gt;Hugo&lt;/a&gt; today, but any framework of your choice will suffice&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☐ &lt;strong&gt;Setup a new AWS CDK project&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;install everything &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/home.html" rel="noopener noreferrer"&gt;AWS CDK&lt;/a&gt; related, configure AWS credentials and set up a new CDK TypeScript project&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☐ &lt;strong&gt;Define AWS resources for the deployment&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;deep dive into the CDK app, take a look at &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/stacks.html" rel="noopener noreferrer"&gt;stacks&lt;/a&gt; and define all the necessary &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/constructs.html" rel="noopener noreferrer"&gt;CDK constructs&lt;/a&gt; for a static site deploy (&lt;code&gt;s3.bucket&lt;/code&gt;, &lt;code&gt;cloudfront.Distribution&lt;/code&gt;, etc.)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☐ &lt;strong&gt;Deploy the site&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;run our first deployment with the &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/cli.html" rel="noopener noreferrer"&gt;CDK Toolkit commands&lt;/a&gt;, view the deployed stack and resources inside the AWS Management Console&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☐ &lt;strong&gt;Security and Domain Name&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;scan our site with &lt;a href="https://observatory.mozilla.org/" rel="noopener noreferrer"&gt;Mozilla Observatory&lt;/a&gt; and improve our grade by registering a domain name, enabling HTTPS, adding a certificate and setting security headers&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a demo static site
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ If you already have a static site ready, you can skip this todo and proceed to &lt;strong&gt;Setup a new AWS CDK project&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the purposes of this tutorial, we will not be focusing on fine-tuning the site contents. Today, I will be deploying a simple static blog and have chosen to use &lt;a href="https://gohugo.io/" rel="noopener noreferrer"&gt;Hugo&lt;/a&gt; as my framework (other popular alternatives for this purpose include &lt;a href="https://www.11ty.dev/" rel="noopener noreferrer"&gt;11ty&lt;/a&gt;, &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt;, and &lt;a href="https://jekyllrb.com/" rel="noopener noreferrer"&gt;Jekyll&lt;/a&gt;). I don't need any complex user interactions, so a static site generator is sufficient for my needs.&lt;br&gt;
If you plan to use Hugo as well, make sure it is &lt;a href="https://gohugo.io/installation" rel="noopener noreferrer"&gt;installed&lt;/a&gt; on your machine. Then, open up a terminal (on Windows, it's recommended to use an Unix command line terminal or Powershell; &lt;a href="https://www.atlassian.com/git/tutorials/git-bash" rel="noopener noreferrer"&gt;Git Bash&lt;/a&gt; is usually the easiest option),&lt;/p&gt;

&lt;p&gt;and run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hugo version
&lt;span class="c"&gt;# hugo v0.103.0+extended darwin/arm64 BuildDate=unknown&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;to verify your installation. You should get a similar output to the one above.&lt;/p&gt;

&lt;p&gt;Now that the hard part is done, we can generate our site by running&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hugo new site my-blog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Running the command above should generate a new &lt;code&gt;my-blog&lt;/code&gt; folder within the current directory. Let's move inside that folder and pull in a theme from GitHub (you can see a full list of available themes &lt;a href="https://themes.gohugo.io/" rel="noopener noreferrer"&gt;here&lt;/a&gt;). I'll be using the &lt;a href="https://themes.gohugo.io/themes/hugo-paper/" rel="noopener noreferrer"&gt;Paper&lt;/a&gt; theme today&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;my-blog
git init
git submodule add https://github.com/nanxiaobei/hugo-paper themes/paper
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You should see a new subfolder under &lt;code&gt;themes&lt;/code&gt;, in my case it is called &lt;code&gt;themes/paper&lt;/code&gt;. Since we are using Git submodules to manage themes, you should also see a new &lt;code&gt;.gitmodules&lt;/code&gt; file being generated in the root directory.&lt;/p&gt;

&lt;p&gt;While we're at it, let's also add a &lt;code&gt;.gitignore&lt;/code&gt; file for Hugo. Create a new file&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; .gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Similar to other frameworks, we do not want to include any build files or automatically generated files in our repository. In Hugo, the build folder is called &lt;code&gt;/public&lt;/code&gt; by default, so let's add that to our &lt;code&gt;.gitignore&lt;/code&gt; file to exclude it from version control&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generated files by hugo&lt;/span&gt;
/public/
&lt;span class="c"&gt;# Temporary lock file while building&lt;/span&gt;
/.hugo_build.lock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now that we've taken care of the Git-related stuff, let's apply the theme to our project. To do this, we just need to add a new line to the configuration file. Open up &lt;code&gt;config.toml&lt;/code&gt; and add the following line&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;theme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"paper"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/config.toml#L4" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To add some content, use the &lt;code&gt;new&lt;/code&gt; command to create a new Markdown file inside the &lt;code&gt;content&lt;/code&gt; folder&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hugo new posts/my-first-post.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Hugo has excellent Markdown support and will automatically convert the contents of the file into a new page at &lt;code&gt;/posts/my-first-post/&lt;/code&gt;. Let's verify that by starting up Hugo's development server:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hugo server &lt;span class="nt"&gt;-D&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="c"&gt;# Web Server is available at http://localhost:1313/&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When you start the server, you should see some basic information output to the terminal. Look for the &lt;code&gt;localhost&lt;/code&gt; URL. If you are not running anything on Hugo's default &lt;a href="https://gohugo.io/commands/hugo_server/#options" rel="noopener noreferrer"&gt;port&lt;/a&gt; (&lt;code&gt;1313&lt;/code&gt;), the site should now be available at that URL. Open it up in a browser. With the Paper theme, the posts should also be visible on the home page by default. Visit &lt;code&gt;/posts/my-first-post&lt;/code&gt; and edit the &lt;code&gt;/content/posts/my-first-post.md&lt;/code&gt; file, then save your changes. Hugo should rebuild the site without stopping the server, and you should see your changes reflected on the site.&lt;/p&gt;

&lt;p&gt;All right we are ready to ship this thing 🚀, run&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hugo &lt;span class="nt"&gt;-D&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By default, when you use &lt;code&gt;hugo new&lt;/code&gt; to create a new post, Hugo treats it as a draft and sets the &lt;code&gt;draft: true&lt;/code&gt; flag. To include content marked as a draft in the build, we need to specify the &lt;code&gt;-D&lt;/code&gt; option. All of the build files will be located in the &lt;code&gt;/public&lt;/code&gt; directory, and they should be &lt;strong&gt;ready to deploy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Done&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ Create a demo static site&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Setup a new AWS CDK project
&lt;/h2&gt;

&lt;p&gt;To simplify interactions with AWS services, consider installing the &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt;. This will provide you with a set of command-line tools that allow you to easily manage your AWS resources from the terminal.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Access keys consist of an access key ID and secret access key, which are used to sign programmatic requests that you make to AWS. If you don't have access keys, you can create them from the AWS Management Console.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before we can use the AWS CDK, we need to set up AWS credentials for our account. There are several ways to do this, but for our purposes, we will use the &lt;em&gt;"Creating a key pair"&lt;/em&gt; method as described in the AWS documentation. Here's how to do it, sign in to the AWS Management Console and&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="https://console.aws.amazon.com/iam" rel="noopener noreferrer"&gt;IAM console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In the navigation pane, choose &lt;em&gt;&lt;strong&gt;Users&lt;/strong&gt;&lt;/em&gt; and click the &lt;em&gt;&lt;strong&gt;Add Users&lt;/strong&gt;&lt;/em&gt; button.&lt;/li&gt;
&lt;li&gt;Type in a &lt;em&gt;&lt;strong&gt;User name&lt;/strong&gt;&lt;/em&gt; for the key pair, I'm going with "my-blog-cdk". Check &lt;em&gt;&lt;strong&gt;Access key - Programmatic access&lt;/strong&gt;&lt;/em&gt; as the &lt;em&gt;&lt;strong&gt;AWS credential type&lt;/strong&gt;&lt;/em&gt; and hit &lt;em&gt;&lt;strong&gt;Next: Permissions&lt;/strong&gt;&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Now, you could dive deeper with permissions in this step if you have to take security a bit more serious, I'm going to keep it simple, select &lt;em&gt;&lt;strong&gt;Attach existing policies directly&lt;/strong&gt;&lt;/em&gt; and check &lt;em&gt;&lt;strong&gt;AdministratorAccess&lt;/strong&gt;&lt;/em&gt;. Click &lt;em&gt;&lt;strong&gt;Next: Tags&lt;/strong&gt;&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;We can skip &lt;em&gt;&lt;strong&gt;Tags&lt;/strong&gt;&lt;/em&gt; and click &lt;em&gt;&lt;strong&gt;Next: Review&lt;/strong&gt;&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Finally click &lt;em&gt;&lt;strong&gt;Create user&lt;/strong&gt;&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Now you can either download the .&lt;em&gt;&lt;strong&gt;csv&lt;/strong&gt;&lt;/em&gt; file and use that for the configuration or simply copy-paste the &lt;em&gt;&lt;strong&gt;access key ID&lt;/strong&gt;&lt;/em&gt; and &lt;em&gt;&lt;strong&gt;secret access key&lt;/strong&gt;&lt;/em&gt; pair. ❗ Be careful with exposing the keys if you followed my steps and chose &lt;em&gt;&lt;strong&gt;AdministratorAccess&lt;/strong&gt;&lt;/em&gt; for permissions, since anyone could access your AWS resources with the key pair.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that we have our AWS credentials, we can configure them for use with the AWS CDK. The easiest way to do this is to run the AWS CLI &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html" rel="noopener noreferrer"&gt;configure&lt;/a&gt; command. On MacOS, the configuration file will be stored at &lt;code&gt;~/.aws/credentials&lt;/code&gt;, so you may need to use sudo to run this command (for more information on configuration and credential file settings, see this &lt;a href="(https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)"&gt;page&lt;/a&gt;). Alternatively, you can create the &lt;code&gt;.aws&lt;/code&gt; files manually if you are not using the AWS CLI&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;aws configure
&lt;span class="c"&gt;# AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE&lt;/span&gt;
&lt;span class="c"&gt;# AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY&lt;/span&gt;
&lt;span class="c"&gt;# Default region name [None]: us-east-1&lt;/span&gt;
&lt;span class="c"&gt;# Default output format [None]: json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When prompted, enter your access key ID and secret access key from the key pair that you created earlier. For the region name, enter &lt;code&gt;us-east-1&lt;/code&gt;, and for the output format, enter &lt;code&gt;json&lt;/code&gt;. If you run &lt;code&gt;aws configure&lt;/code&gt; again or &lt;code&gt;cat ~/.aws/credentials&lt;/code&gt;, you should see your configured credentials.&lt;/p&gt;

&lt;p&gt;Alright we got the keys 🔑, let's move on and install the &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/work-with.html" rel="noopener noreferrer"&gt;AWS CDK Toolkit&lt;/a&gt; (globally) with &lt;code&gt;npm&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; aws-cdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you were following the previous todo, our project folder structure currently looks something like this&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;my-blog
│   ...
├── public &lt;span class="c"&gt;# Hugo build folder&lt;/span&gt;
│   ...
└── .gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The name of the build folder (in this case, &lt;code&gt;public&lt;/code&gt;) is not particularly important, but it's important that we specify the correct path to the folder when we configure our deployment later. Let's create a new &lt;code&gt;cdk&lt;/code&gt; folder at the same level as the &lt;code&gt;public&lt;/code&gt; folder and initialize a new empty CDK project using the &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/cli.html#cli-init" rel="noopener noreferrer"&gt;cdk init&lt;/a&gt; command&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;cdk
&lt;span class="nb"&gt;cd &lt;/span&gt;cdk
&lt;span class="nb"&gt;sudo &lt;/span&gt;cdk init app &lt;span class="nt"&gt;--language&lt;/span&gt; typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In addition to other generated files and folders, we should focus on the &lt;code&gt;cdk.json&lt;/code&gt; file first. This is a &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/cli.html#cli-config" rel="noopener noreferrer"&gt;configuration file&lt;/a&gt; for the CDK, and it tells the CDK Toolkit how to execute our app. It contains information such as the programming language being used, the app's entry point, and other runtime settings.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If one of &lt;code&gt;cdk.json&lt;/code&gt; or &lt;code&gt;~/.cdk.json&lt;/code&gt; exists, options specified there will be used as defaults. Settings in &lt;code&gt;cdk.json&lt;/code&gt; take precedence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When we choose &lt;code&gt;TypeScript&lt;/code&gt; as the programming language with &lt;code&gt;cdk init&lt;/code&gt;, it sets the &lt;code&gt;app&lt;/code&gt; option to &lt;code&gt;"app": "npx ts-node --prefer-ts-exts bin/cdk.ts",&lt;/code&gt;. This specifies the command that will be used to execute the CDK application.&lt;/p&gt;

&lt;p&gt;The command specified in the &lt;code&gt;app&lt;/code&gt; option uses &lt;a href="https://github.com/TypeStrong/ts-node" rel="noopener noreferrer"&gt;ts-node&lt;/a&gt; by default, which is an execution engine for Node.js that allows you to run TypeScript code directly. The &lt;code&gt;--prefer-ts-exts&lt;/code&gt; flag prevents &lt;code&gt;ts-node&lt;/code&gt; from prioritizing precompiled &lt;code&gt;.js&lt;/code&gt; files and will always import the TypeScript source code instead, if it is available. This is useful if you are also using &lt;code&gt;tsc&lt;/code&gt; (the TypeScript compiler) alongside the &lt;code&gt;app&lt;/code&gt; option. The &lt;code&gt;bin/cdk.ts&lt;/code&gt; file is the entry point for our CDK app, which defines the main function that will be executed when the app is run.&lt;/p&gt;

&lt;p&gt;To make it easier to use certain values throughout our CDK app, let's define some environment variables. Create a new &lt;code&gt;.env&lt;/code&gt; file inside the &lt;code&gt;cdk&lt;/code&gt; folder and add the following lines (we will use these later)&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CDK_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us-east-1
&lt;span class="c"&gt;# Add your AWS Account ID here,&lt;/span&gt;
&lt;span class="c"&gt;# can be grabbed from AWS Management Console when,&lt;/span&gt;
&lt;span class="c"&gt;# clicking on your profile in the top right dropdown&lt;/span&gt;
&lt;span class="nv"&gt;CDK_ACCOUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2383838383
&lt;span class="c"&gt;# Optional, if you are planning to purchase or have a domain&lt;/span&gt;
&lt;span class="c"&gt;# ready to use&lt;/span&gt;
&lt;span class="nv"&gt;DOMAIN_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;e53nec.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Even though our &lt;code&gt;.env&lt;/code&gt; file does not contain any sensitive information, it is a good practice to exclude it from version control. To do this, open the &lt;code&gt;cdk/.gitignore&lt;/code&gt; file and add the following line&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# env files&lt;/span&gt;
.env&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To make it easier to access the environment variables defined in the &lt;code&gt;.env&lt;/code&gt; file, we will use the &lt;a href="https://github.com/motdotla/dotenv" rel="noopener noreferrer"&gt;dotenv&lt;/a&gt; module. This module loads environment variables from a &lt;code&gt;.env&lt;/code&gt; file into our CDK app. To install dotenv, run the following command&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;dotenv &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To use dotenv in our CDK app, we need to import and configure it in the entry point file (&lt;code&gt;bin/cdk.ts&lt;/code&gt;). At the top of this file, add the following lines&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/bin/cdk.ts#L3" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will import the dotenv module and use the &lt;code&gt;config&lt;/code&gt; function to load the environment variables from the &lt;code&gt;.env&lt;/code&gt; file. We will be able to access these variables throughout our CDK app using &lt;code&gt;process.env.VARIABLE_NAME&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To verify that the environment variables are being loaded correctly, we can log them to the console and execute the app with &lt;code&gt;ts-node&lt;/code&gt;. Inside &lt;code&gt;bin/cdk.ts&lt;/code&gt;, add the following line below the dotenv configuration&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CDK_REGION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Remove if it's working&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// Initialize a CDK application&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and run&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ts-node bin/cdk.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You should see &lt;code&gt;us-east-1&lt;/code&gt; printed to the console. Now that we have set up our CDK project and configured the environment variables, we are ready to start building our CDK app by writing CDK constructs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Done&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ Setup a new AWS CDK project&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Define AWS resources for the deployment
&lt;/h2&gt;

&lt;p&gt;At the top level of every CDK App we define one or multiple &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/stacks.html" rel="noopener noreferrer"&gt;stacks&lt;/a&gt;. Stacks are units of deployments, they are used for organizing and grouping the AWS resources together to a single deployment. In a TypeScript CDK app, any instance of the &lt;code&gt;cdk.Stack&lt;/code&gt; class represents a stack. You can see an example of this with the &lt;code&gt;CdkStack&lt;/code&gt; class in the &lt;code&gt;bin/cdk.ts&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CdkStack&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../lib/cdk-stack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CdkStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CdkStack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;CdkStack&lt;/code&gt; class extends the root &lt;code&gt;cdk.Stack&lt;/code&gt; class, which is imported from the &lt;code&gt;aws-cdk-lib&lt;/code&gt; module in the &lt;code&gt;lib/cdk-stack.ts&lt;/code&gt; file. This allows us to define custom behavior for our stack in addition to the functionality provided by the base &lt;code&gt;cdk.Stack&lt;/code&gt; class&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CdkStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Inside stacks, we define &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/constructs.html" rel="noopener noreferrer"&gt;constructs&lt;/a&gt;, which are the basic building blocks of a CDK app. Constructs represent a &lt;em&gt;cloud component&lt;/em&gt; (I'm going to use the terms &lt;em&gt;construct&lt;/em&gt; and &lt;em&gt;components&lt;/em&gt; interchangeably), such as an &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html" rel="noopener noreferrer"&gt;S3 Bucket&lt;/a&gt; or a &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.Distribution.html" rel="noopener noreferrer"&gt;CloudFront Distribution&lt;/a&gt;. These components are turned into AWS resources after the CDK app is deployed.&lt;/p&gt;

&lt;p&gt;For those who prefer visual representations, the composition of an app with stacks and constructs can be summarized with the following diagram from the AWS docs&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fug7dcjkufif95y410wne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fug7dcjkufif95y410wne.png" alt="AWS CDK structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are now going to add some constructs to our stacks. The following structure is optional, but we will define a &lt;code&gt;BaseStack&lt;/code&gt; which will contain all the components that can be reused across multiple site deployments and a &lt;code&gt;BlogStack&lt;/code&gt; for components specific to our blog. This separation of reusable and specific components allows for greater flexibility and modularity in our app in case we plan on having multiple deployments in the future.&lt;/p&gt;

&lt;p&gt;Let's rename the file &lt;code&gt;lib/cdk-stack.ts&lt;/code&gt; to &lt;code&gt;lib/base-stack.ts&lt;/code&gt; and the exported &lt;code&gt;CdkStack&lt;/code&gt; class to &lt;code&gt;BaseStack&lt;/code&gt;. We will remove all unnecessary comments and add our first construct to the &lt;code&gt;BaseStack&lt;/code&gt;, which will be the storage for the site: an &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html" rel="noopener noreferrer"&gt;S3 Bucket&lt;/a&gt; 🪣&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-s3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BaseBucket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/base-stack.ts#L35" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Constructs are implemented in classes that extend the &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/constructs.Construct.html" rel="noopener noreferrer"&gt;Construct&lt;/a&gt; base class. You define a construct by instantiating the class.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We have instantiated the &lt;code&gt;s3.Bucket&lt;/code&gt; class, thus defining a new &lt;code&gt;Bucket&lt;/code&gt; construct. Inside our &lt;code&gt;BaseStack&lt;/code&gt;, we will also define private properties for the constructs. These will be exposed and passed down to the child &lt;code&gt;BlogStack&lt;/code&gt; later. All constructs take three parameters when they are initialized: the &lt;code&gt;scope&lt;/code&gt;, the &lt;code&gt;id&lt;/code&gt;, and the &lt;code&gt;props&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BaseBucket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;scope&lt;/strong&gt; - The first parameter is the &lt;code&gt;scope&lt;/code&gt;, which is the construct's parent or owner. This can be either a stack or another construct (in our case, the &lt;code&gt;BaseStack&lt;/code&gt; will be the owner of the &lt;code&gt;Bucket&lt;/code&gt; construct). In JavaScript (TypeScript), we use the &lt;code&gt;this&lt;/code&gt; keyword to represent the current object for the scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;id&lt;/strong&gt; - The second parameter is the &lt;code&gt;id&lt;/code&gt;, which is a unique identifier within the current scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;props&lt;/strong&gt; - The third parameter is the &lt;code&gt;props&lt;/code&gt;, which is a set of properties that define the construct's configuration. In most cases, constructs provide sensible defaults, and if all &lt;code&gt;props&lt;/code&gt; elements are optional, you can omit the &lt;code&gt;props&lt;/code&gt; parameter completely (which is what we did with the bucket above).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first two parameters will be repetitive across the whole CDK app. To keep the construct ids unique, I will use this simple &lt;code&gt;StackNameConstructName&lt;/code&gt; pattern.&lt;/p&gt;

&lt;p&gt;When deploying a static site to the S3 bucket, we could either&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html#websiteindexdocument" rel="noopener noreferrer"&gt;Enable static website hosting&lt;/a&gt; by adding the &lt;code&gt;websiteIndexDocument&lt;/code&gt; property to our Bucket construct. This would require changing the defaults and some additional configuration when serving the site with CloudFront.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the bucket is configured for website hosting, the CloudFront origin will be configured to use the bucket as an HTTP server origin and will use the bucket's configured website redirects and error handling. Otherwise, the origin is created as a bucket origin and will use CloudFront's redirect and error handling.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep the &lt;code&gt;s3.bucket&lt;/code&gt; as a bucket origin for CloudFront. ✅&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We are going to stick with the defaults and continue with the &lt;strong&gt;2nd&lt;/strong&gt; (bucket origin) option. We could pretty much achieve the same final result with the first one (see &lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/cloudfront-serve-static-website/" rel="noopener noreferrer"&gt;using a website endpoint as the origin&lt;/a&gt;). There are also some caveats to both options, we will go through the latter ones.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html" rel="noopener noreferrer"&gt;default props&lt;/a&gt; for the Bucket should then just work fine. The only interesting ones to us are the &lt;code&gt;blockPublicAccess&lt;/code&gt; and the &lt;code&gt;removalPolicy&lt;/code&gt; properties. If you are only playing around and plan on destroying the bucket later make sure to modify the &lt;code&gt;removalPolicy&lt;/code&gt; and the &lt;code&gt;autoDeleteObjects&lt;/code&gt; properties based on your needs&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This will also destroy the bucket when running cdk destroy later&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BaseBucket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;removalPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RemovalPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DESTROY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;autoDeleteObjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To allow users to access the site's content through CloudFront, we can add another construct called an &lt;code&gt;OriginAccessIdentity&lt;/code&gt;. This is a special CloudFront user that can be associated with an S3 bucket origin. Since the default for &lt;code&gt;blockPublicAccess&lt;/code&gt; is &lt;code&gt;s3.BlockPublicAccess.BLOCK_ALL&lt;/code&gt;, this is necessary.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;aws_cloudfront&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_originAccessIdentity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OriginAccessIdentity&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_originAccessIdentity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OriginAccessIdentity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BaseCfOriginAccessIdentity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/base-stack.ts#L36" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we need to grant our Origin Access Identity (OAI) bucket read permissions. We can do this by adding the user to the bucket resource policy with the &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html#addwbrtowbrresourcewbrpolicypermission" rel="noopener noreferrer"&gt;addToResourcePolicy()&lt;/a&gt; method, passing in a new &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_iam.PolicyStatement.html" rel="noopener noreferrer"&gt;IAM statement&lt;/a&gt; and attaching our OAI to the policy statement principals.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-iam&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToResourcePolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s3:GetObject&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arnForObjects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
    &lt;span class="na"&gt;principals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CanonicalUserPrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_originAccessIdentity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudFrontOriginAccessIdentityS3CanonicalUserId&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/base-stack.ts#L37" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That should be all the permission stuff done. Now we have only have one little problem left that we can solve within our &lt;code&gt;BaseStack&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you have not been following from the beginning, I used Hugo to generate the site. I have this build folder structure (which is common when using static site generators) for my pages&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;public
│   ...
├── posts
│   └── my-first-post
│       └── index.html
│   ...
└── index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Since we chose the bucket origin option, we must request the file at &lt;code&gt;/posts/my-first-post/index.html&lt;/code&gt; directly, and our page routing will not work (this should be handled automatically if the static website hosting option is enabled). However, if we want to access the posts using the &lt;code&gt;/posts/my-first-post/&lt;/code&gt; URL, we can use a &lt;a href="https://aws.amazon.com/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale/" rel="noopener noreferrer"&gt;CloudFront Function&lt;/a&gt; to make this happen.&lt;/p&gt;

&lt;p&gt;To begin, create a new folder called &lt;code&gt;cdk/functions&lt;/code&gt; and add a new file to it called &lt;code&gt;cdk/functions/reqRewrite.js&lt;/code&gt;. By default, a new CDK project includes a &lt;code&gt;*.js&lt;/code&gt; line in the &lt;code&gt;.gitignore&lt;/code&gt; file, which ignores all JavaScript files. In order to ensure that our function code is tracked and committed to the repository, we can add a new rule &lt;code&gt;!/functions/*&lt;/code&gt;, which grabs back all of the &lt;code&gt;.js&lt;/code&gt; files inside the &lt;code&gt;functions&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;In this file, we will rewrite all page requests by checking if the request does not contain a dot (e.g. &lt;code&gt;.css&lt;/code&gt;, &lt;code&gt;.js&lt;/code&gt;). This will help us determine if we are requesting a file or not. If the request does not contain a dot, it means that we are not requesting a file, and we can proceed with the rewrite and append &lt;code&gt;.index.html&lt;/code&gt; to the requested URI&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rewritePageUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;isDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// Handle both "/posts/my-post/" and "/posts/my-post"&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isDir&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;

  &lt;span class="c1"&gt;// Requesting a page ("/" or "/posts/my-post")&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Append index.html to the requested uri&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rewritePageUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/functions/reqRewrite.js#L1" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the page requests will now serve the respective &lt;code&gt;.index.html&lt;/code&gt; files.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ The code provided above is designed to work with a simple build folder structure, such as &lt;code&gt;my-page/index.html&lt;/code&gt; or similar. If you have a different build folder structure, you will need to modify the &lt;code&gt;rewritePageUri()&lt;/code&gt; function so that it appends the correct &lt;code&gt;.html&lt;/code&gt; file to the requested URI. This will ensure that the correct page is displayed when a user navigates to your site.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's go ahead and attach our function to a CloudFront function associations list (we will drop this list into our CloudFront construct later). To do this, create a new &lt;code&gt;_functionAssociations&lt;/code&gt; property and add a new construct as follows&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_functionAssociations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FunctionAssociation&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_functionAssociations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FunctionEventType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VIEWER_REQUEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BaseRequestRewriteFunction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FunctionCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromFile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;functions/reqRewrite.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/base-stack.ts#L48" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is important to note that we need to create a &lt;code&gt;VIEWER_REQUEST&lt;/code&gt; type function in order to modify the request before it reaches the origin(see &lt;a href="https://aws.amazon.com/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale/" rel="noopener noreferrer"&gt;Introducing CloudFront Functions&lt;/a&gt;). To create a &lt;code&gt;VIEWER_REQUEST&lt;/code&gt; type function, we can use the &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.Function.html" rel="noopener noreferrer"&gt;Function&lt;/a&gt; construct, which allows us to specify the source code of the function from a file (such as the &lt;code&gt;reqRewrite.js&lt;/code&gt; file we created earlier). We can also pass the code in as an inline string if we prefer.&lt;/p&gt;

&lt;p&gt;In order to make our base stack resources accessible to other CDK stacks, let's create and export a &lt;code&gt;BaseStackInterface&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;BaseStackResources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&lt;/span&gt;
  &lt;span class="nx"&gt;originAccessIdentity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OriginAccessIdentity&lt;/span&gt;
  &lt;span class="nx"&gt;functionAssociations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FunctionAssociation&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/base-stack.ts#L8" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and add a public &lt;code&gt;resources()&lt;/code&gt; method to the &lt;code&gt;BaseStack&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_functionAssociations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FunctionAssociation&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="nf"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;BaseStackResources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;originAccessIdentity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_originAccessIdentity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;functionAssociations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_functionAssociations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/base-stack.ts#L23" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By doing this, we can ensure that our child stacks are able to leverage the resources created in the &lt;code&gt;BaseStack&lt;/code&gt;, making our cdk project more modular and reusable.&lt;/p&gt;

&lt;p&gt;Great, it looks like we have all the base resources we need. Now, let's create our stack. To do this, open the &lt;code&gt;bin/cdk.ts&lt;/code&gt; file and instantiate the &lt;code&gt;BaseStack&lt;/code&gt; class as follows (make sure to use unique stack IDs if you are planning on deploying multiple CDK applications to the same AWS account)&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BaseStack&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../lib/base-stack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseStack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BaseStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AwsStaticSiteStarterBaseStack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/bin/cdk.ts#L15" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is a good practice to ensure that our stacks are deployed to the desired &lt;code&gt;account&lt;/code&gt; and &lt;code&gt;region&lt;/code&gt; every time. To accomplish this, we can use the &lt;code&gt;cdk.StackProps.env&lt;/code&gt; and retrieve the values from our &lt;code&gt;.env&lt;/code&gt; file, and pass them as properties to our stacks. This approach is recommended for production environments, as it ensures consistency and eliminates potential issues that may arise when working with other team members who may have different accounts configured&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sharedProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StackProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Set the region/account fields of env to either a concrete&lt;/span&gt;
  &lt;span class="c1"&gt;// value to select the indicated environment (recommended for production stacks)&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;account&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CDK_ACCOUNT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CDK_REGION&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseStack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BaseStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AwsStaticSiteStarterBaseStack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;sharedProps&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/bin/cdk.ts#L15" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have finished preparing resources for the CloudFront construct, we can move on to creating our &lt;code&gt;BlogStack&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To get started, create a new file called &lt;code&gt;lib/blog-stack.ts&lt;/code&gt; and define a new class called &lt;code&gt;BlogStack&lt;/code&gt;. The stack props should contain the additional &lt;code&gt;BaseStackResources&lt;/code&gt; props. We can then destructure the props to get the individual resource instances as follows&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BaseStackResources&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./base-stack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BlogStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StackProps&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;BaseStackResources&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;originAccessIdentity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;functionAssociations&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/blog-stack.ts#L15" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have defined our &lt;code&gt;BlogStack&lt;/code&gt; class, we can create an instance of it inside &lt;code&gt;bin/cdk.ts&lt;/code&gt; and pass in the resources as props&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BlogStack&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../lib/blog-stack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BlogStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AwsStaticSiteStarterBlogStack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;sharedProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;baseStack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/bin/cdk.ts#L17" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To finish setting up the &lt;code&gt;BlogStack&lt;/code&gt;, let's go back to &lt;code&gt;lib/blog-stack.ts&lt;/code&gt; and create a &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.Distribution.html" rel="noopener noreferrer"&gt;CloudFront distribution&lt;/a&gt;. We are going to specify a few props here&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;defaultRootObject&lt;/code&gt; - The root &lt;code&gt;index.html&lt;/code&gt; object we want to request from the S3 origin when a viewer requests the root URL for our distribution.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;defaultBehavior&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;functionAssociations&lt;/code&gt; - The request rewrite function that we created earlier.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;origin&lt;/code&gt; - A new &lt;code&gt;S3Origin&lt;/code&gt; construct with our bucket and the &lt;code&gt;originAccessIdentity&lt;/code&gt; from our base stack.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;origins&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-cloudfront-origins&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;aws_cloudfront&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cfDistribution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Distribution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BlogCfDistribution&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;defaultRootObject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;defaultBehavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;functionAssociations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;functionAssociations&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;origins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;S3Origin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;originAccessIdentity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;originPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/blog-stack.ts#L18" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, we will also need a &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3_deployment.BucketDeployment.html" rel="noopener noreferrer"&gt;BucketDeployment&lt;/a&gt; construct to deploy our site to our S3 bucket (populate the S3 bucket with our &lt;code&gt;public&lt;/code&gt; folder). As for the &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3_deployment.BucketDeployment.html#construct-props" rel="noopener noreferrer"&gt;props&lt;/a&gt;, the &lt;code&gt;destinationBucket&lt;/code&gt; and the &lt;code&gt;distribution&lt;/code&gt; are the obvious ones. The ones that need a little bit more attention would be&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;distributionPaths&lt;/code&gt; - The file paths to invalidate in the CloudFront distribution. This is important to specify because if the cache is not invalidated, users may continue to see the old content, which can lead to a variety of issues, such as incorrect display of content, broken links, JavaScript errors, etc.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;destinationKeyPrefix&lt;/code&gt; - This is to organize our objects in the bucket by placing them in a specific subdirectory (&lt;code&gt;blog&lt;/code&gt;). In case we would want to deploy multiple apps to the same bucket, we could specify different prefixes for different apps.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sources&lt;/code&gt; - The &lt;code&gt;sources&lt;/code&gt; property allows us to specify the files or directories that we want to deploy to the bucket. An array of &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3_deployment.ISource.html" rel="noopener noreferrer"&gt;ISource&lt;/a&gt; objects is expected, so we can use the &lt;code&gt;s3deploy.Source.asset()&lt;/code&gt; with the node.js &lt;code&gt;path.join()&lt;/code&gt; to build the path to our &lt;code&gt;public&lt;/code&gt; directory.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;s3deploy&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-s3-deployment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;s3deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BucketDeployment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BlogBucketDeployment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;destinationBucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;distribution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cfDistribution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;distributionPaths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;destinationKeyPrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;s3deploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/blog-stack.ts#L40" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;BaseStack&lt;/code&gt; and &lt;code&gt;BlogStack&lt;/code&gt; structure that I chose should now be clearer. If we decide to deploy another site, we can easily reuse resources from the &lt;code&gt;BaseStack&lt;/code&gt; and add an additional child stack (similar to or the same as the &lt;code&gt;BlogStack&lt;/code&gt;) as needed. We can then modify the paths and prefixes as needed.&lt;/p&gt;




&lt;div class="ltag__link"&gt;
  &lt;a href="/htmnk" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F989502%2Fa07a597a-2896-4f8c-8f57-dd4c0af2a536.jpeg" alt="htmnk"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/htmnk/caching-site-assets-with-aws-cdk-s3-snippet-3hp9" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Caching Site Assets with AWS CDK &amp;amp; S3 - snippet&lt;/h2&gt;
      &lt;h3&gt;Erik Petrinec ・ Feb 1 '23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#aws&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#performance&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#typescript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;p&gt;Deploying to CloudFront CDN results in a significant improvement over other alternatives due to Amazon's edge cache. To further improve our site performance, we must set &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control" rel="noopener noreferrer"&gt;Cache-Control&lt;/a&gt; headers that control caching of our assets inside browsers. You may want to consider the snippet I provided above, which extends the &lt;code&gt;BucketDeployment&lt;/code&gt; (drop-in &lt;a href="https://gist.github.com/htmnk/507ece0ecf98d1dd0cb4aa94f53a291b#file-cdk-stack-ts" rel="noopener noreferrer"&gt;SiteBucketDeployment&lt;/a&gt; construct) with several additional properties that enable you to set different &lt;code&gt;Cache-Control&lt;/code&gt; headers according to the specified file extensions.&lt;/p&gt;




&lt;p&gt;Well that was easy, let's ship this thing for real now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Done&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ Define AWS resources for the deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deploy the site
&lt;/h2&gt;

&lt;p&gt;If this is your first time deploying a CDK app on your AWS account, you may also need to run the &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html" rel="noopener noreferrer"&gt;cdk bootstrap&lt;/a&gt; command. This command creates the necessary resources in your account to support CDK deployments, such as an S3 bucket for storing templates and assets. A simple&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cdk bootstrap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;should work if your credentials are configured. You should also get a warning when trying to deploy without bootstrapping first.&lt;/p&gt;

&lt;p&gt;There are also some useful commands that we can run to ensure that the cdk app is in good shape and we can catch any potential issues early on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cdk diff&lt;/code&gt; - This command compares the current state of the application with the deployed state and shows you the differences between them. This can help you understand what changes will be made to your resources during the next deployment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cdk synth&lt;/code&gt; - Generates the CloudFormation template for your stack, which can be useful for reviewing the template, or even debugging and troubleshooting issues related to your stack deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to the built-in CDK commands, we can also include a custom &lt;code&gt;build&lt;/code&gt; command to catch any errors or issues with the application's code before deployment. Since it is not part of the CDK toolchain, and we are using &lt;code&gt;ts-node&lt;/code&gt;, we can replace the build script in the &lt;code&gt;package.json&lt;/code&gt; file with the command we used in the previous section&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx ts-node bin/cdk.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/package.json#L8" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright it's finally time to deploy. As we have multiple stacks within the app, it is necessary to use the &lt;code&gt;cdk deploy --all&lt;/code&gt; option when deploying our app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# sudo cdk deploy --all&lt;/span&gt;
cdk deploy &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When deploying the CDK application for the first time, it may take a while as all the resources defined in the stacks will be created. During the deployment process, you may be prompted with confirmation messages, asking you to confirm the creation of certain resources.&lt;/p&gt;

&lt;p&gt;If the deployment process is successful, you should see a green checkmark with the stack id in the console&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;...

✅ AwsStaticSiteStarterBlogStack

✨ Deployment &lt;span class="nb"&gt;time&lt;/span&gt;: 326.15s

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's explore the deployed resources in the AWS Management Console. We can first visit the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html" rel="noopener noreferrer"&gt;AWS CloudFormation&lt;/a&gt; service that displays a list of all the stacks in our AWS account, along with the resources that are associated with each stack&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the AWS Management Console&lt;/li&gt;
&lt;li&gt;In the top navigation bar, click on the Services menu and search/select CloudFormation.&lt;/li&gt;
&lt;li&gt;In the CloudFormation console, you will see a list of all the stacks in your AWS account.&lt;/li&gt;
&lt;li&gt;To view the resources associated with a stack, click on the stack name.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We can also view the properties and status of each resource in our stack, as well as perform various actions such as updating or deleting the stack. This is particularly useful when integrating CDK into a continuous integration and deployment (CI/CD) workflow. If a deployment gets stuck, we can use the AWS Management Console to take manual action to fix the issue.&lt;/p&gt;

&lt;p&gt;To access our deployed blog, we will need to use the URL provided by CloudFront. To find the HTTP URL of our CloudFront distribution&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select/Search CloudFront from the services menu.&lt;/li&gt;
&lt;li&gt;In the CloudFront console, you will see a list of all the CloudFront distributions in your AWS account.&lt;/li&gt;
&lt;li&gt;Click on the distribution that was created by your CDK app.&lt;/li&gt;
&lt;li&gt;In the distribution details page, you will see the Domain Name of the distribution, and it will take the form of &lt;em&gt;&lt;strong&gt;xxxxxxx.cloudfront.net&lt;/strong&gt;&lt;/em&gt;. We can use this URL to access our blog using HTTP. Go ahead and paste the url inside your browser.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are not using Hugo as we did in the first section, your site may already be working as intended. With Hugo it should be completely broken at this stage, we have to go back to our &lt;code&gt;config.toml&lt;/code&gt; and update the &lt;a href="https://gohugo.io/getting-started/configuration/#baseurl" rel="noopener noreferrer"&gt;baseURL&lt;/a&gt; with our generated URL&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;baseURL&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'http://xxxxxxx.cloudfront.net'&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/config.toml#L1" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's rebuild and redeploy the site again (make sure you are inside the root directory)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hugo &lt;span class="nt"&gt;-D&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;cdk
cdk deploy &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The redeploy should be noticeably quicker this time because we are only updating the contents of the site, not the constructs.&lt;/p&gt;

&lt;p&gt;🎉 That's it!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ If you encounter issues when attempting to redeploy and the &lt;code&gt;cdk diff&lt;/code&gt; command shows changes to the &lt;code&gt;AWS::Cloudfront::Function .zip&lt;/code&gt;, one solution is to make a small modification to the function code, such as adding a comment. Another option is to use the inline version of the function code when creating the CloudFront function. This may resolve the issue and allow for successful redeployment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For those of you that are only playing around and don't want to keep the deployed stacks, you can use the &lt;code&gt;cdk destroy&lt;/code&gt; command to delete all the resources that were created by the deployment. You might have to edit and redeploy the &lt;code&gt;s3.bucket&lt;/code&gt; construct (the bucket needs to be empty) so that it gets properly destroyed&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BaseBucket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;removalPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RemovalPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DESTROY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;autoDeleteObjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cdk destroy &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively you can also delete the contents of the bucket inside the AWS Management console and destroy the stack manually with CloudFormation.&lt;/p&gt;

&lt;p&gt;If you have purchased a domain through AWS and want to take your site to production by enabling HTTPS, this final section below is for you. Even if you are not planning to purchase a domain at this time, you may still want to read along to learn about how to set up HTTPS and other security related features using the CDK.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Done&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ Deploy the site&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security and Domain Name
&lt;/h2&gt;

&lt;p&gt;Let's evaluate the security of our site by using &lt;a href="https://observatory.mozilla.org/" rel="noopener noreferrer"&gt;Mozilla Observatory&lt;/a&gt; to obtain a security grade. Simply paste your CloudFront &lt;code&gt;http://xxxxxxx.cloudfront.net/&lt;/code&gt; URL to the input. At this stage, it is likely that we will receive an &lt;strong&gt;"F"&lt;/strong&gt; rating, similar to a failing grade in college. However, unlike academic studies, it does not require extensive effort to improve our score. The most important and immediate step is to implement HTTPS, which is now a standard even for websites that do not transmit sensitive data. Failure to do so can result in Google Chrome flagging the website as insecure, which is not an acceptable outcome for a publicly accessible site.&lt;/p&gt;

&lt;p&gt;Before diving deep into certificates and HTTPS, we can perform a security checklist by following the suggestions of Mozilla Observatory. One way to do this is by using the &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.ResponseHeadersPolicy.html" rel="noopener noreferrer"&gt;ResponseHeadersPolicy&lt;/a&gt; construct to add several security headers to our CloudFront distribution&lt;/p&gt;

&lt;p&gt;These headers include (checkout the &lt;a href="https://infosec.mozilla.org/guidelines/web_security#web-security-cheat-sheet" rel="noopener noreferrer"&gt;Web Security Cheat Sheet&lt;/a&gt; for more details, the importance and implementation difficulty of each one):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Content-Security-Policy&lt;/code&gt; - used to restrict the resources that a browser can load for a given page&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Content-Type-Options&lt;/code&gt; - prevents the browser from interpreting files as something other than declared by the content type&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Frame-Options&lt;/code&gt; - prevents clickjacking by ensuring that the content is not embedded into other sites&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Referrer-Policy&lt;/code&gt; - allows us to control the value of the referrer header&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Strict-Transport-Security&lt;/code&gt; - used to enforce secure connections to the server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;X-XSS-Protection&lt;/code&gt; - helps prevent cross-site scripting attacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Go back to our &lt;code&gt;cdk/lib/base-stack.ts&lt;/code&gt; and create the &lt;code&gt;ResponseHeadersPolicy&lt;/code&gt; construct (we will skip CSP in this article)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;BaseStackResources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nx"&gt;responseHeadersPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseHeadersPolicy&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_responseHeadersPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseHeadersPolicy&lt;/span&gt;

  &lt;span class="nf"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;BaseStackResources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;
      &lt;span class="na"&gt;responseHeadersPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_responseHeadersPolicy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_responseHeadersPolicy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ResponseHeadersPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BaseResponseHeadersPolicy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;responseHeadersPolicyName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CustomBaseResponseHeadersPolicy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;securityHeadersBehavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/**
         * @todo ResponseHeadersPolicy CSP (for you, in case you want to have * a perfect grade)
         *
         * When using Hugo, implementing a proper Content Security Policy (CSP) can be
         * challenging when there is limited control over the scripts included on the site.
         */&lt;/span&gt;
        &lt;span class="c1"&gt;// contentSecurityPolicy: { contentSecurityPolicy: 'default-src https:;', override: true },&lt;/span&gt;
        &lt;span class="na"&gt;contentTypeOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;override&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;frameOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;frameOption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HeadersFrameOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DENY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;override&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;referrerPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;referrerPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HeadersReferrerPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NO_REFERRER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;override&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;strictTransportSecurity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;override&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;accessControlMaxAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;365&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;includeSubdomains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;xssProtection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;override&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;protection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;modeBlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/base-stack.ts#L56" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before proceeding further, ensure that your domain name is registered with Amazon Route 53. This process also creates a &lt;a href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/hosted-zones-working-with.html" rel="noopener noreferrer"&gt;hosted zone&lt;/a&gt; with your domain name as part of the registration. You can follow the guide &lt;a href="https://aws.amazon.com/getting-started/hands-on/get-a-domain/" rel="noopener noreferrer"&gt;How to Register a Domain Name with Amazon Route 53&lt;/a&gt;) if you haven't done so already.&lt;/p&gt;

&lt;p&gt;We can utilize the &lt;code&gt;HostedZone.fromLookup()&lt;/code&gt; method to create a &lt;code&gt;HostedZone&lt;/code&gt; construct, which is required to specify for our certificate. Simply pass in your domain name, which should also be stored in the &lt;code&gt;.env&lt;/code&gt; file at this point if you have been following the guide from the beginning&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;route53&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-route53&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;BaseStackResources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;route53&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IHostedZone&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_zone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;route53&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IHostedZone&lt;/span&gt;

  &lt;span class="nf"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;BaseStackResources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;
      &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_zone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;route53&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HostedZone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromLookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BaseHostedZone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;domainName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DOMAIN_NAME&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/base-stack.ts#L81" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we will once again proceed to the &lt;code&gt;lib/blog-stack.ts&lt;/code&gt; file to bring everything together in our CloudFront distribution. We can retrieve the &lt;code&gt;zone&lt;/code&gt; and &lt;code&gt;responseHeadersPolicy&lt;/code&gt; constructs we created previously from the props. We can also grab the shared &lt;code&gt;env&lt;/code&gt; prop for the &lt;code&gt;region&lt;/code&gt;. Additionally, let's assign the value of &lt;code&gt;process.env.DOMAIN_NAME&lt;/code&gt; to a variable, as we will be using it in multiple places within the &lt;code&gt;BlogStack&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;responseHeadersPolicy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;originAccessIdentity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;functionAssociations&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;domainName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DOMAIN_NAME&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/blog-stack.ts#L15" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alright, let's pass in the base stack constructs and specify some additional parameters for the CloudFront distribution. These include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.BehaviorOptions.html#responseheaderspolicy" rel="noopener noreferrer"&gt;defaultBehavior.responseHeadersPolicy&lt;/a&gt; - this is where we will pass in our &lt;code&gt;responseHeadersPolicy&lt;/code&gt; construct&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.BehaviorOptions.html#viewerprotocolpolicy" rel="noopener noreferrer"&gt;defaultBehavior.viewerProtocolPolicy&lt;/a&gt; - set to &lt;code&gt;cf.ViewerProtocolPolicy.REDIRECT_TO_HTTPS&lt;/code&gt; to redirect all HTTP requests to HTTPS&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.Distribution.html#priceclass" rel="noopener noreferrer"&gt;priceClass&lt;/a&gt; - set to &lt;code&gt;cf.PriceClass.PRICE_CLASS_ALL&lt;/code&gt; for all CloudFront locations&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.Distribution.html#domainnames" rel="noopener noreferrer"&gt;domainNames&lt;/a&gt; - an array containing our site's domain name, passed in as &lt;code&gt;[domainName]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.Distribution.html#certificate" rel="noopener noreferrer"&gt;certificate&lt;/a&gt; - it is necessary to specify the certificate that should be used for HTTPS connections. This is done by passing in an instance of the &lt;code&gt;Certificate&lt;/code&gt; construct as a property of the &lt;code&gt;Distribution&lt;/code&gt; construct. The certificate is then associated with the distribution, and CloudFront uses it to authenticate connections to our website. The certificate must be located in N. Virginia &lt;code&gt;us-east-1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.Distribution.html#errorresponses" rel="noopener noreferrer"&gt;errorResponses&lt;/a&gt; - CloudFront, with our configuration, will return a &lt;code&gt;403&lt;/code&gt; error for any missing requests. This includes the standard &lt;code&gt;404&lt;/code&gt; page responses. To properly handle these errors, we can use this behavior to remap the response and return a &lt;code&gt;/404.html&lt;/code&gt; page. Keep in mind that this page must exist on your site.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;acm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-certificatemanager&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cfDistribution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Distribution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BlogCfDistribution&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;defaultRootObject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;defaultBehavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;functionAssociations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;functionAssociations&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;origins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;S3Origin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;originAccessIdentity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;originPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nx"&gt;responseHeadersPolicy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;viewerProtocolPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ViewerProtocolPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIRECT_TO_HTTPS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;priceClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PriceClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRICE_CLASS_ALL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;domainNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;domainName&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;certificate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;acm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DnsValidatedCertificate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BlogCertificate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;domainName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// Support www. + domain name&lt;/span&gt;
    &lt;span class="na"&gt;subjectAlternativeNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;www.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;domainName&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;hostedZone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;errorResponses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;httpStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;responseHttpStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;responsePagePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/404.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/blog-stack.ts#L18" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next up: an &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_route53.ARecord.html" rel="noopener noreferrer"&gt;ARecord&lt;/a&gt;. An alias record in Route 53 is a type of record that can be used to map a domain name to selected AWS resources, such as an Elastic Load Balancer, CloudFront distribution, or an S3 bucket. We can use this construct to map our domain name to a &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_route53_targets.CloudFrontTarget.html" rel="noopener noreferrer"&gt;CloudFrontTarget&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;route53&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-route53&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;route53targets&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-route53-targets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;route53&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ARecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BlogAliasRecord&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;recordName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;domainName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;route53&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RecordTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAlias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;route53targets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CloudFrontTarget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cfDistribution&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/blog-stack.ts#L48" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not crucial, but we can also use the &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_route53_patterns.HttpsRedirect.html" rel="noopener noreferrer"&gt;HttpsRedirect&lt;/a&gt; construct to redirect requests made to the www version of our domain to the non-www version. This is often done to ensure that users accessing our site are directed to a consistent URL, and to improve SEO. By using this construct, we can easily configure a redirect from &lt;code&gt;www.domainName.com&lt;/code&gt; to &lt;code&gt;domainName.com&lt;/code&gt; for all requests made to our site&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;aws_cloudfront&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aws_route53_patterns&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;aws_route53_patterns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HttpsRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BlogWwwToNonWww&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;recordNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;www.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;domainName&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;targetDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;domainName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/htmnk/aws-cdk-static-site-starter/blob/main/cdk/lib/blog-stack.ts#L54" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The construct uses CloudFront and S3 behind the scenes, so an additional CloudFront distribution and S3 bucket will be created.&lt;/p&gt;

&lt;p&gt;That should be everything we need for now. Let's try a redeploy with the command &lt;code&gt;cdk deploy --all&lt;/code&gt;. If the deployment is successful, you should be able to access your site at your purchased domain over HTTPS.&lt;/p&gt;

&lt;p&gt;After deploying our CDK app, we can view the created certificate in the &lt;a href="https://docs.aws.amazon.com/acm/latest/userguide/acm-overview.html" rel="noopener noreferrer"&gt;AWS Certificate Manager&lt;/a&gt; and the created records inside the hosted zone in the &lt;a href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/Welcome.html" rel="noopener noreferrer"&gt;AWS Route 53&lt;/a&gt; service.&lt;/p&gt;

&lt;p&gt;Let's also run a scan on the site using Mozilla Observatory again. We should see an improvement in the security grade, and should now receive a grade of &lt;strong&gt;"B"&lt;/strong&gt; this time.&lt;/p&gt;

&lt;p&gt;🥳 Here's the final result &lt;a href="https://e53nec.com/" rel="noopener noreferrer"&gt;e53nec.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Done&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☑ Security and Domain Name&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://e53nec.com/snippets/caching-site-assets-with-aws-cdk-s3/" rel="noopener noreferrer"&gt;Caching Site Assets with AWS CDK &amp;amp; S3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
