{"componentChunkName":"component---src-templates-blog-post-js","path":"/serverless-website-sam-aws-part-2/","result":{"data":{"site":{"siteMetadata":{"title":"Fabrizio Fortunato","author":"Fabrizio Fortunato","siteUrl":"https://izifortune.com"}},"markdownRemark":{"id":"9d30df88-9952-5c49-a61d-801e94757f59","excerpt":"In one of my previous articles Build a serverless website with SAM on AWS,\nI’ve explained how we can start creating a serverless website and what are the…","html":"<p>In one of my previous articles <a href=\"https://izifortune.com/serverless-website-sam-aws/\">Build a serverless website with SAM on AWS</a>,\nI’ve explained how we can start creating a serverless website and what are the\nadvantages of adopting a serverless architecture. Through <em>sam-cli</em> and\n<em>Cloudformation</em> templates we created an <em>S3</em> bucket and a <em>Cloudfront</em>\ndistribution. Now we will extend the previous architecture adding more\nfunctionality to our serverless website</p>\n<p>In this article, we will explore how to create path rewrites (mod rewrites) and\nsecurity headers using Lambda at the Edge (L@E). We will finish then attaching a\ncustom domain name and taking care of  assets caching to improve the performance of\nour serverless website.</p>\n<h2>Rewrites</h2>\n<p>Following the previous article, we have our website infrastructure at this point\nand its deployed in <em>AWS. Cloudfront</em> is acting as a web server for your website\nand forwards the requests to the origin following the behaviours declared\nbefore. When navigating to HTML pages a classical webserver applies a series of\n<em>rewrites</em> to the request before fetching the necessary resources. A good\nexample is <em>mod_rewrite</em> for Apache, in fact, we never request the homepage of a\nwebsite using a full resource location such as\n<em>www.izifortune.com/index.html</em> but\nrather using a shorter format like <em>www.izifortune.com</em>. <em>Cloudfront</em> provides\nsome basic functionalities using <code class=\"language-text\">DefaultRootObject: index.html</code> attribute,\nwhich will point to a root index.html when we are not specifying any path in the\nURL.</p>\n<p>Once we start supporting multiple pages on our website, using\n<code class=\"language-text\">DefaultRootObject</code> is not enough, to rewrite all the possible paths. A custom\nsolution is needed that can mimic a behaviour similar to <em>mod_rewrite</em>.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/a07218fb45e4bb2fceb60eb8e0f43c42/9c991/rewrites.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 58.10810810810811%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAAA60lEQVQoz6VSTU/DMAztrwcJcYL/s8MO+x9oArW0SdV82rGT4K5TOzhMnXiKIx/es5+dNPUfaCRyzqWUe6xShYCIRPRX3Hbfepxqycbg+1t6foLXF2xbSinECAjJKIZAw9APaqyxq2Q2sXVOWJIg8unEhwMfj2ytGKI5En1+9DHg7LFUM5yD6TfxfYhhzlRKXvIICRNt4tuBc76e2yUw80pjlnHS3s4C7/2yMK11d8E0TVJxl1hkcsuqQwjeeWstAMg6domFuo7mogWMD9imC5b8azwr1y9v3+z8TGtnpZUYf6Dzryrzb7wW+gG9/8JsqkSsUQAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Serverless rewrites\"\n        title=\"Serverless rewrites\"\n        src=\"/dist/static/a07218fb45e4bb2fceb60eb8e0f43c42/102e5/rewrites.png\"\n        srcset=\"/dist/static/a07218fb45e4bb2fceb60eb8e0f43c42/83276/rewrites.png 148w,\n/dist/static/a07218fb45e4bb2fceb60eb8e0f43c42/ba02f/rewrites.png 295w,\n/dist/static/a07218fb45e4bb2fceb60eb8e0f43c42/102e5/rewrites.png 590w,\n/dist/static/a07218fb45e4bb2fceb60eb8e0f43c42/74549/rewrites.png 885w,\n/dist/static/a07218fb45e4bb2fceb60eb8e0f43c42/7213b/rewrites.png 1180w,\n/dist/static/a07218fb45e4bb2fceb60eb8e0f43c42/9c991/rewrites.png 3254w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>We can use <a href=\"https://aws.amazon.com/lambda/edge/\">Lambda at the Edge</a> (L@E) to\nimplement our custom business logic between Cloudfront and the origin, in our\ncase S3 Bucket. L@E is a functionality of <em>Cloudfront</em> that lets you run our\ncode on the <em>edge.</em> An <em>edge</em> is a <a href=\"https://aws.amazon.com/cloudfront/features/#edge-locations\">Cloudfront Regional\nCache</a>. You can use\ndifferent runtimes but mainly you are restricted to use node or python for the\nfunctions and you can see a full list of requirements and restrictions for L@E\n<a href=\"https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-requirements-limits.html\">here</a>.\nIn the article, we will be using node runtime for the example functions. </p>\n<p>The rewrite functionality can be then implemented with the following code:</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token string\">'use strict'</span><span class=\"token punctuation\">;</span> exports<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">lambdaHandler</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">event</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n\n  <span class=\"token comment\">// Extract the request from the CloudFront event that is sent to</span>\n  Lambda@Edge <span class=\"token keyword\">const</span> request <span class=\"token operator\">=</span> event<span class=\"token punctuation\">.</span>Records<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>cf<span class=\"token punctuation\">.</span>request<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Extract the URI from the request const olduri = request.uri;</span>\n\n  <span class=\"token comment\">// Match any '/' that occurs at the end of a URI. Replace it with a</span>\n  <span class=\"token keyword\">default</span> index <span class=\"token keyword\">const</span> newuri <span class=\"token operator\">=</span> olduri<span class=\"token punctuation\">.</span><span class=\"token function\">replace</span><span class=\"token punctuation\">(</span><span class=\"token regex\">/\\/$/</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'\\/index.html'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Replace the received URI with the URI that includes the index page</span>\n  request<span class=\"token punctuation\">.</span>uri <span class=\"token operator\">=</span> newuri<span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Return to CloudFront return request;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Save the lambda at the edge code using the following structure: <code class=\"language-text\">rewrite/app.js</code>\nand then add the lambda as a resource, together with the necessary role, inside\nthe <code class=\"language-text\">template.yaml</code></p>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\">    <span class=\"token punctuation\">...</span> \n<span class=\"token key atrule\">Resources</span><span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">...</span> \n    \n  <span class=\"token key atrule\">LambdaEdgeFunctionRole</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">Type</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"AWS::IAM::Role\"</span>\n    <span class=\"token key atrule\">Properties</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">Path</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'/'</span>\n      <span class=\"token key atrule\">ManagedPolicyArns</span><span class=\"token punctuation\">:</span>\n        <span class=\"token punctuation\">-</span> <span class=\"token string\">\"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole\"</span>\n      <span class=\"token key atrule\">AssumeRolePolicyDocument</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">Version</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"2012-10-17\"</span>\n        <span class=\"token key atrule\">Statement</span><span class=\"token punctuation\">:</span>\n          <span class=\"token punctuation\">-</span>\n            <span class=\"token key atrule\">Sid</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"AllowLambdaServiceToAssumeRole\"</span>\n            <span class=\"token key atrule\">Effect</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"Allow\"</span>\n            <span class=\"token key atrule\">Action</span><span class=\"token punctuation\">:</span> \n              <span class=\"token punctuation\">-</span> <span class=\"token string\">\"sts:AssumeRole\"</span>\n            <span class=\"token key atrule\">Principal</span><span class=\"token punctuation\">:</span>\n              <span class=\"token key atrule\">Service</span><span class=\"token punctuation\">:</span> \n                <span class=\"token punctuation\">-</span> <span class=\"token string\">\"lambda.amazonaws.com\"</span>\n                <span class=\"token punctuation\">-</span> <span class=\"token string\">\"edgelambda.amazonaws.com\"</span>\n\n  <span class=\"token key atrule\">RewriteLambda</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">Type</span><span class=\"token punctuation\">:</span> AWS<span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span>Serverless<span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span>Function\n    <span class=\"token key atrule\">Properties</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">CodeUri</span><span class=\"token punctuation\">:</span> rewrite/\n      <span class=\"token key atrule\">Description</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'Serverless rewrite lambda'</span>\n      <span class=\"token key atrule\">Handler</span><span class=\"token punctuation\">:</span> app.lambdaHandler\n      <span class=\"token key atrule\">Runtime</span><span class=\"token punctuation\">:</span> nodejs12.x\n      <span class=\"token key atrule\">MemorySize</span><span class=\"token punctuation\">:</span> <span class=\"token number\">128</span>\n      <span class=\"token key atrule\">Timeout</span><span class=\"token punctuation\">:</span> <span class=\"token number\">1</span>\n      <span class=\"token key atrule\">Role</span><span class=\"token punctuation\">:</span> <span class=\"token tag\">!GetAtt</span> LambdaEdgeFunctionRole.Arn\n      <span class=\"token key atrule\">AutoPublishAlias</span><span class=\"token punctuation\">:</span> live\n    <span class=\"token punctuation\">...</span></code></pre></div>\n<p>We now need to attach the function to Cloudfront, so that it will be possible to\nintercept the requests and rewrite to the relevant resources. A L@E can be\nattached at different points in a request flow:</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/edda98a3546e7047d449f606d44b14ca/8a5e6/lambda-at-the-edge-events.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 43.24324324324324%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAAA30lEQVQoz6VQy24DIQzc///FXqJUaRVps7Bg8AObrSmVmqQ9tb5gM54ZmOX4Ry1H791PAcJSK7bW7tCBHA2oQq31EZrkuWWWUwohlFJU9X6jm+actm1zaBp9k5klZxgGOkpEENFHrCps3vile5oZETlq3fYS1IbBEuL+enl7kux2bFeBXX/+U1RO60vCOMiulwGQiJndY2jbMLy+rzFkb0qpIm2Wj75/vpwQ6/zz8FRKVPOnAs+giKk1GX1jLgFdnshHl3aV+dIvsqcSY/BUAOB4jtvNYb3dYoy/p/23+gAVSRJkR/nFWQAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Lambda at the edge events\"\n        title=\"Lambda at the edge events\"\n        src=\"/dist/static/edda98a3546e7047d449f606d44b14ca/102e5/lambda-at-the-edge-events.png\"\n        srcset=\"/dist/static/edda98a3546e7047d449f606d44b14ca/83276/lambda-at-the-edge-events.png 148w,\n/dist/static/edda98a3546e7047d449f606d44b14ca/ba02f/lambda-at-the-edge-events.png 295w,\n/dist/static/edda98a3546e7047d449f606d44b14ca/102e5/lambda-at-the-edge-events.png 590w,\n/dist/static/edda98a3546e7047d449f606d44b14ca/74549/lambda-at-the-edge-events.png 885w,\n/dist/static/edda98a3546e7047d449f606d44b14ca/7213b/lambda-at-the-edge-events.png 1180w,\n/dist/static/edda98a3546e7047d449f606d44b14ca/8a5e6/lambda-at-the-edge-events.png 2752w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>The hooks provided by Cloudfront are divided into Viewer and Origin and then\ndivided once more by request and response:</p>\n<ul>\n<li><strong>Viewer request</strong> - Once Cloudfront receive the initial request</li>\n<li><strong>Origin request</strong> - Before Cloudfront send the request to the origin</li>\n<li><strong>Origin response</strong>  - After Cloudfront receive the response from the origin</li>\n<li><strong>Viewer response</strong> - Before Cloudfront returns the response.</li>\n</ul>\n<p>If you want to read more about how to use the hooks you can visit the page\n<a href=\"https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-at-the-edge.html\">Customzing Content at the Edge</a>.\nIn our example, we will attach the L@E to an <em>Origin Request.</em></p>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token punctuation\">...</span>\n  <span class=\"token key atrule\">WebsiteCloudfrontDistribution</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">Type</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"AWS::CloudFront::Distribution\"</span>\n    <span class=\"token key atrule\">Properties</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">DistributionConfig</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">Aliases</span><span class=\"token punctuation\">:</span>\n            <span class=\"token punctuation\">-</span> &lt;ADD YOU ALIASES HERE<span class=\"token punctuation\">></span>\n        <span class=\"token key atrule\">Comment</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"Cloudfront distribution for serverless website\"</span>\n        <span class=\"token key atrule\">ViewerCertificate</span><span class=\"token punctuation\">:</span>\n          <span class=\"token key atrule\">AcmCertificateArn</span><span class=\"token punctuation\">:</span> &lt;CERT HERE<span class=\"token punctuation\">></span>\n          <span class=\"token key atrule\">MinimumProtocolVersion</span><span class=\"token punctuation\">:</span> TLSv1.1_2016\n          <span class=\"token key atrule\">SslSupportMethod</span><span class=\"token punctuation\">:</span> sni<span class=\"token punctuation\">-</span>only\n        <span class=\"token key atrule\">DefaultRootObject</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"index.html\"</span>\n        <span class=\"token key atrule\">Enabled</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">true</span>\n        <span class=\"token key atrule\">HttpVersion</span><span class=\"token punctuation\">:</span> http2\n        <span class=\"token key atrule\">Origins</span><span class=\"token punctuation\">:</span>\n          <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">Id</span><span class=\"token punctuation\">:</span> s3<span class=\"token punctuation\">-</span>website\n            <span class=\"token key atrule\">DomainName</span><span class=\"token punctuation\">:</span> <span class=\"token tag\">!GetAtt</span> Bucket.DomainName\n            <span class=\"token key atrule\">S3OriginConfig</span><span class=\"token punctuation\">:</span> \n              <span class=\"token key atrule\">OriginAccessIdentity</span><span class=\"token punctuation\">:</span> \n                <span class=\"token key atrule\">Fn::Sub</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}'</span>\n        <span class=\"token key atrule\">DefaultCacheBehavior</span><span class=\"token punctuation\">:</span>\n          <span class=\"token key atrule\">Compress</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'true'</span>\n          <span class=\"token key atrule\">AllowedMethods</span><span class=\"token punctuation\">:</span>\n            <span class=\"token punctuation\">-</span> GET\n            <span class=\"token punctuation\">-</span> HEAD\n            <span class=\"token punctuation\">-</span> OPTIONS\n          <span class=\"token key atrule\">ForwardedValues</span><span class=\"token punctuation\">:</span>\n            <span class=\"token key atrule\">QueryString</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">false</span>\n          <span class=\"token key atrule\">TargetOriginId</span><span class=\"token punctuation\">:</span> s3<span class=\"token punctuation\">-</span>website\n          <span class=\"token key atrule\">ViewerProtocolPolicy</span> <span class=\"token punctuation\">:</span> redirect<span class=\"token punctuation\">-</span>to<span class=\"token punctuation\">-</span>https\n          <span class=\"token key atrule\">LambdaFunctionAssociations</span><span class=\"token punctuation\">:</span>\n            <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">EventType</span><span class=\"token punctuation\">:</span> origin<span class=\"token punctuation\">-</span>request\n              <span class=\"token key atrule\">LambdaFunctionARN</span><span class=\"token punctuation\">:</span> <span class=\"token tag\">!Ref</span> RewriteLambda.Version</code></pre></div>\n<p>Noticed that we can only attach a specific <em>Version</em> of the function in\n<em>Cloudfront</em>. Using the attribute <code class=\"language-text\">AutoPublishAlias:live</code> in the lambda\ndefinition <em>SAM</em> takes care of publishing and giving us the latest version.</p>\n<p>We can start testing out the lambda packaging and deploying it with <em>SAM.</em> Since\nwe will need a package and deploy multiple times we can create scripts to avoid\nrepeating long commands:</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token comment\"># package.sh sam package --output-template-file packaged.yaml --s3-bucket</span>\n<span class=\"token operator\">&lt;</span>YOURSWEBSITE.COM<span class=\"token operator\">></span>-sam\n\n<span class=\"token comment\"># deploy.sh sam deploy --template-file packaged.yaml --stack-name</span>\n<span class=\"token operator\">&lt;</span>yourwebsite<span class=\"token operator\">></span> --capabilities CAPABILITY_IAM --region us-east-1\",</code></pre></div>\n<p>And then proceed to invoke the commands:</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">sh</span> package.sh <span class=\"token operator\">&amp;&amp;</span> <span class=\"token function\">sh</span> deploy.sh</code></pre></div>\n<h2>Security headers</h2>\n<p>While developing a website it is important to consider also security for the\nwebsite itself and our users. Today’s standards include having your website\nusing <em>HTTPS</em> protocol which you can achieve by attaching a valid certificate to\nCloudfront distribution which we will see later when looking to add a domain. A\nsometimes overlooked practice which can boost the security of your website is\nusing correct security headers. A full list of headers can be found in the\n<a href=\"https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#tab=Headers\">OWASP Secure Headers\nProject</a>.\n<em>HTTP</em> headers can be used to restrict modern browsers from running\nvulnerabilities.</p>\n<p>Attaching headers to a response is the job a web server but in our case, we\ndon’t have any. That’s where a L@E can help us out. Using the <em>Viewer Response</em>\nhook, which triggers before <em>Cloudfront</em> return the response we can add the\nadditional security headers to the response.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/f6f6a10b9d9c7636601e123621f998d4/9c991/secure-headers.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 58.10810810810811%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAABD0lEQVQoz6VS20oEMQydr1fQN/0fZV/2P2TdhXGuvUzTNE3HdAo7WnBRPNASmpPkJGmz/gONnJRxk5UyBxGJqA4uoBi0xuencH/nHx/wo+WUggNADHqI3lHfd/0wrdCupPdgpY1dnBiI8Xjk1xc6HKK1IoZjZAp0fuvAoRA4rbo/Od3twZJwnOZNfybUkhNLBrlLg+ADBvrac371ANlv39dgHARjtDFmWZaYwYUj/hgphFD3zBvm7mzmniJrpcZxBABxSYoyMHlpNyilJGVTiYwxMdejl7A8USLn3GIXUeS9l0rNj9vZRJZbqNdlWjAeoZZ9A7Sh2JfpNNiu7L755We6Vh7GQYT/ofK3LLz/x09vQcCkdgqIcwAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Serverless security headers\"\n        title=\"Serverless security headers\"\n        src=\"/dist/static/f6f6a10b9d9c7636601e123621f998d4/102e5/secure-headers.png\"\n        srcset=\"/dist/static/f6f6a10b9d9c7636601e123621f998d4/83276/secure-headers.png 148w,\n/dist/static/f6f6a10b9d9c7636601e123621f998d4/ba02f/secure-headers.png 295w,\n/dist/static/f6f6a10b9d9c7636601e123621f998d4/102e5/secure-headers.png 590w,\n/dist/static/f6f6a10b9d9c7636601e123621f998d4/74549/secure-headers.png 885w,\n/dist/static/f6f6a10b9d9c7636601e123621f998d4/7213b/secure-headers.png 1180w,\n/dist/static/f6f6a10b9d9c7636601e123621f998d4/9c991/secure-headers.png 3254w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>The function below contains all the necessary logic:</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\">exports<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">lambdaHandler</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">event</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> Records<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> cf<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> response <span class=\"token punctuation\">}</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> event<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">if</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">[</span><span class=\"token string\">'content-type'</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">&amp;&amp;</span> response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">[</span><span class=\"token string\">'content-type'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">.</span><span class=\"token function\">indexOf</span><span class=\"token punctuation\">(</span><span class=\"token string\">'text/html'</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;</span> <span class=\"token number\">0</span> <span class=\"token operator\">||</span>\n    response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">[</span><span class=\"token string\">'location'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> response<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  response<span class=\"token punctuation\">.</span>headers <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">...</span>response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">[</span><span class=\"token string\">'strict-transport-security'</span><span class=\"token punctuation\">]</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span>key<span class=\"token operator\">:</span> <span class=\"token string\">'Strict-Transport-Security'</span><span class=\"token punctuation\">,</span> value<span class=\"token operator\">:</span> <span class=\"token string\">'max-age=63072000; includeSubdomains'</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">[</span><span class=\"token string\">'x-content-type-options'</span><span class=\"token punctuation\">]</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span>key<span class=\"token operator\">:</span> <span class=\"token string\">'X-Content-Type-Options'</span><span class=\"token punctuation\">,</span> value<span class=\"token operator\">:</span> <span class=\"token string\">'nosniff'</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">[</span><span class=\"token string\">'x-frame-options'</span><span class=\"token punctuation\">]</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span>key<span class=\"token operator\">:</span> <span class=\"token string\">'X-Frame-Options'</span><span class=\"token punctuation\">,</span> value<span class=\"token operator\">:</span> <span class=\"token string\">'SAMEORIGIN'</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">[</span><span class=\"token string\">'x-xss-protection'</span><span class=\"token punctuation\">]</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span>key<span class=\"token operator\">:</span> <span class=\"token string\">'X-XSS-Protection'</span><span class=\"token punctuation\">,</span> value<span class=\"token operator\">:</span> <span class=\"token string\">'1; mode=block'</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">[</span><span class=\"token string\">'referrer-policy'</span><span class=\"token punctuation\">]</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span>key<span class=\"token operator\">:</span> <span class=\"token string\">'Referrer-Policy'</span><span class=\"token punctuation\">,</span> value<span class=\"token operator\">:</span> <span class=\"token string\">'same-origin'</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">return</span> response<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Similar to the previous L@E that we created for rewrites we need now to create a\n<em>Resource</em> and attach the L@E to our Cloudfront distribution.</p>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\">..\n  <span class=\"token key atrule\">WebsiteCloudfrontDistribution</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">Type</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"AWS::CloudFront::Distribution\"</span>\n    <span class=\"token key atrule\">Properties</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">DistributionConfig</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">Aliases</span><span class=\"token punctuation\">:</span>\n            <span class=\"token punctuation\">-</span> &lt;ADD YOU ALIASES HERE<span class=\"token punctuation\">></span>\n        <span class=\"token key atrule\">Comment</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"Cloudfront distribution for serverless website\"</span>\n        <span class=\"token key atrule\">ViewerCertificate</span><span class=\"token punctuation\">:</span>\n          <span class=\"token key atrule\">AcmCertificateArn</span><span class=\"token punctuation\">:</span> &lt;CERT HERE<span class=\"token punctuation\">></span>\n          <span class=\"token key atrule\">MinimumProtocolVersion</span><span class=\"token punctuation\">:</span> TLSv1.1_2016\n          <span class=\"token key atrule\">SslSupportMethod</span><span class=\"token punctuation\">:</span> sni<span class=\"token punctuation\">-</span>only\n        <span class=\"token key atrule\">DefaultRootObject</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"index.html\"</span>\n        <span class=\"token key atrule\">Enabled</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">true</span>\n        <span class=\"token key atrule\">HttpVersion</span><span class=\"token punctuation\">:</span> http2\n        <span class=\"token key atrule\">Origins</span><span class=\"token punctuation\">:</span>\n          <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">Id</span><span class=\"token punctuation\">:</span> s3<span class=\"token punctuation\">-</span>website\n            <span class=\"token key atrule\">DomainName</span><span class=\"token punctuation\">:</span> <span class=\"token tag\">!GetAtt</span> Bucket.DomainName\n            <span class=\"token key atrule\">S3OriginConfig</span><span class=\"token punctuation\">:</span> \n              <span class=\"token key atrule\">OriginAccessIdentity</span><span class=\"token punctuation\">:</span> \n                <span class=\"token key atrule\">Fn::Sub</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}'</span>\n        <span class=\"token key atrule\">DefaultCacheBehavior</span><span class=\"token punctuation\">:</span>\n          <span class=\"token key atrule\">Compress</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'true'</span>\n          <span class=\"token key atrule\">AllowedMethods</span><span class=\"token punctuation\">:</span>\n            <span class=\"token punctuation\">-</span> GET\n            <span class=\"token punctuation\">-</span> HEAD\n            <span class=\"token punctuation\">-</span> OPTIONS\n          <span class=\"token key atrule\">ForwardedValues</span><span class=\"token punctuation\">:</span>\n            <span class=\"token key atrule\">QueryString</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">false</span>\n          <span class=\"token key atrule\">TargetOriginId</span><span class=\"token punctuation\">:</span> s3<span class=\"token punctuation\">-</span>website\n          <span class=\"token key atrule\">ViewerProtocolPolicy</span> <span class=\"token punctuation\">:</span> redirect<span class=\"token punctuation\">-</span>to<span class=\"token punctuation\">-</span>https\n          <span class=\"token key atrule\">LambdaFunctionAssociations</span><span class=\"token punctuation\">:</span>\n            <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">EventType</span><span class=\"token punctuation\">:</span> origin<span class=\"token punctuation\">-</span>request\n              <span class=\"token key atrule\">LambdaFunctionARN</span><span class=\"token punctuation\">:</span> <span class=\"token tag\">!Ref</span> RewriteLambda.Version\n            <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">EventType</span><span class=\"token punctuation\">:</span> viewer<span class=\"token punctuation\">-</span>response\n              <span class=\"token key atrule\">LambdaFunctionARN</span><span class=\"token punctuation\">:</span> <span class=\"token tag\">!Ref</span> SecureHeadersLambda.Version</code></pre></div>\n<p>We have to package and build our <em>SAM</em> application once again:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">sh package.sh &amp;&amp; sh deploy.sh</code></pre></div>\n<h2>Caching</h2>\n<p>A proper caching strategy is essential for any website no matter the number of\nvisits per day. Defining caching for your assets can really make the difference\nfor your users, which they will save bandwidth and improve performance. A\nrequest in fact doesn’t need to be fetched from the origin every time.\nCloudfront honour the <em>Cache-Control</em> header of a response and it will keep the\nrequested asset in a regional cache until the <a href=\"https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Expiration.html\">cache duration\nexpires</a>.\nMoreover, it can reduce the load to our servers, or in our case our bill, since\nwe are paying every request that arrives at the S3 Bucket. If you want to read\nmore about <em>Cache-Control</em> header I can’t recommend enough the article from\n<em>@csswizardry</em> <a href=\"https://csswizardry.com/2019/03/cache-control-for-civilians/\">Cache-Control for\nCivilians</a>.</p>\n<p>While Cloudfront allows to overrides the <em>Cache-Control</em> headers through a\nbehaviour, I normally recommend setting a <em>Cache-Control</em> header on an object\nbase level rather than through a behaviour. This way you don’t need to manage\nmultiple behaviours on different objects.</p>\n<p>Cloudfront can read the object metadata, specifically the <em>Cache-Control</em>\nmetadata that is set in an S3 asset and will use it as <em>Cache-Control</em> header\nfor the response.</p>\n<p>Depending on the assets that you are uploading you can have a different\n<em>Cache-Control</em> associate to them.</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">aws s3 <span class=\"token function\">sync</span> public/ s3://<span class=\"token operator\">&lt;</span>YOURSWEBSITE.COM<span class=\"token operator\">></span> --cache-control <span class=\"token string\">\"public, max-age=604800, must-revalidate\"</span> --acl <span class=\"token string\">\"public-read\"</span> --exclude <span class=\"token string\">\"*\"</span> --include <span class=\"token string\">\"*.html\"</span> --include <span class=\"token string\">\"*.xml\"</span> --include <span class=\"token string\">\"sw.js\"</span> --include <span class=\"token string\">\"robots.txt\"</span> --include <span class=\"token string\">\"favicon.ico\"</span> --include <span class=\"token string\">\"manifest.webmanifest\"</span> --include <span class=\"token string\">\"idb-keyval-iife.min.js\"</span>\n\naws s3 <span class=\"token function\">sync</span> public/ s3://<span class=\"token operator\">&lt;</span>YOURSWEBSITE.COM<span class=\"token operator\">></span> --exclude <span class=\"token string\">\"*.html\"</span> --exclude <span class=\"token string\">\"*.xml\"</span> --exclude <span class=\"token string\">\"sw.js\"</span> --exclude <span class=\"token string\">\"robots.txt\"</span> --exclude <span class=\"token string\">\"favicon.ico\"</span> --exclude <span class=\"token string\">\"manifest.webmanifest\"</span> --exclude <span class=\"token string\">\"idb-keyval-iife.min.js\"</span> --cache-control <span class=\"token string\">\"public,max-age=31536000,immutable\"</span> --acl <span class=\"token string\">\"public-read\"</span></code></pre></div>\n<p>We are caching here the HTML pages for 7 days and all the other assets instead\nfor 1 year.</p>\n<h2>Domain name</h2>\n<p>Now that the basic resources are defined I want to have a closer look at how to\nlink your domain name to a serverless infrastructure. I will take advantage of\nAWS <em>Route 53</em> to generate the necessary <em>RecordSet</em> and <em>Certificate Manager</em>\nto create an SSL certificate. If you don’t have your domain registered in <em>AWS</em>\nyou simply need to change the <em>Nameservers</em> to point to your public hosted zone\nwhich you will create.</p>\n<p>There is one manual step which I’m keeping outside of the final template, the\nSSL certificate creation. The certificate needs to be validated through email or\nDNS for the domains specified and it will fail to create the <em>Cloudfront</em>\ndistribution if it is not verified. For this reason, you can simply go to\n<a href=\"https://console.aws.amazon.com/acm/home?region=us-east-1#/\">https://console.aws.amazon.com/acm/home?region=us-east-1#/</a>\nand register a new certificate. Make sure the region is <em>N.Virginia</em> because\n<em>Cloudfront</em> will only accept certificates from that region. If you want to\nserve your website using <em>www</em> remember to include it also inside the list of\nthe accepted domains.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/2b51bfce2e5b348f343ec05ee333eb00/ffbc4/ssl.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 33.108108108108105%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAABBElEQVQY022Py26DMBBF/dcskAq/AGzKv8CGHa1SZNLyUIkMlIfCG9skoEqoEypVTelZWB6f8dUMKoqi7/vhP8ZxPDfVa+SXZQn3fQMCwRi7blzugZpOrObdsiy3cteAvgXnHCLgnO65TOAmUHDfWwR5TdMkSVJVFaX0jwYLP09RlGVZmqbQBtP+jHAb23VdQghlFOrPX8zzDC8Q6jhO6Pth4MeEMDrOV9iIwx4oz3OMsWmaiqJomva4oeu6qqq2bcNSVV1n7kviOu+HpzLw6EcypPGYxlNKEAR3bWdZliiKkiQ9bMiyLAiCYRjrujZtV+DD2Xlu33DvHzsP98Fx8Fx+Cr8APUN12eL5ZkwAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"AWS Certificate ARN\"\n        title=\"AWS Certificate ARN\"\n        src=\"/dist/static/2b51bfce2e5b348f343ec05ee333eb00/102e5/ssl.png\"\n        srcset=\"/dist/static/2b51bfce2e5b348f343ec05ee333eb00/83276/ssl.png 148w,\n/dist/static/2b51bfce2e5b348f343ec05ee333eb00/ba02f/ssl.png 295w,\n/dist/static/2b51bfce2e5b348f343ec05ee333eb00/102e5/ssl.png 590w,\n/dist/static/2b51bfce2e5b348f343ec05ee333eb00/74549/ssl.png 885w,\n/dist/static/2b51bfce2e5b348f343ec05ee333eb00/7213b/ssl.png 1180w,\n/dist/static/2b51bfce2e5b348f343ec05ee333eb00/ffbc4/ssl.png 1243w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>Once it is created and verified we can copy the <em>arn</em> and include it in your\ntemplate. Now we can add the SSL certificate and the domain name aliases to the\n<em>Cloudfront</em> distribution.</p>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token punctuation\">...</span>\n  <span class=\"token key atrule\">WebsiteCloudfrontDistribution</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">Type</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"AWS::CloudFront::Distribution\"</span>\n    <span class=\"token key atrule\">Properties</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">DistributionConfig</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">Aliases</span><span class=\"token punctuation\">:</span>\n            <span class=\"token punctuation\">-</span> &lt;ADD YOU ALIASES HERE<span class=\"token punctuation\">></span>\n        <span class=\"token key atrule\">Comment</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"Cloudfront distribution for serverless website\"</span>\n        <span class=\"token key atrule\">ViewerCertificate</span><span class=\"token punctuation\">:</span>\n          <span class=\"token key atrule\">AcmCertificateArn</span><span class=\"token punctuation\">:</span> &lt;CERT HERE<span class=\"token punctuation\">></span>\n          <span class=\"token key atrule\">MinimumProtocolVersion</span><span class=\"token punctuation\">:</span> TLSv1.1_2016\n          <span class=\"token key atrule\">SslSupportMethod</span><span class=\"token punctuation\">:</span> sni<span class=\"token punctuation\">-</span>only</code></pre></div>\n<p>After the certificate is associated we need to add an <em>HostedZone</em>, a record\ncontainer in <em>Route53</em> where you can control how to route the traffic fo a\nspecific domain through <em>RecordSet.</em></p>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\">  <span class=\"token key atrule\">HostedZone</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">Type</span><span class=\"token punctuation\">:</span> AWS<span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span>Route53<span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span>HostedZone\n    <span class=\"token key atrule\">Properties</span><span class=\"token punctuation\">:</span> \n      <span class=\"token key atrule\">HostedZoneConfig</span><span class=\"token punctuation\">:</span> \n        <span class=\"token key atrule\">Comment</span><span class=\"token punctuation\">:</span> yourwebsite.com hosted zone\n      <span class=\"token key atrule\">Name</span><span class=\"token punctuation\">:</span> yourwebsite.com\n  <span class=\"token key atrule\">RecordA</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">Type</span><span class=\"token punctuation\">:</span> AWS<span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span>Route53<span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span>RecordSet\n    <span class=\"token key atrule\">DependsOn</span><span class=\"token punctuation\">:</span> WebsiteCloudfrontDistribution\n    <span class=\"token key atrule\">Properties</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">HostedZoneId</span><span class=\"token punctuation\">:</span> <span class=\"token tag\">!Ref</span> HostedZone\n      <span class=\"token key atrule\">Name</span><span class=\"token punctuation\">:</span> &lt;yourwebsite<span class=\"token punctuation\">></span>.com\n      <span class=\"token key atrule\">Type</span><span class=\"token punctuation\">:</span> A\n      <span class=\"token key atrule\">AliasTarget</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">DNSName</span><span class=\"token punctuation\">:</span> <span class=\"token tag\">!GetAtt</span> WebsiteCloudfrontDistribution.DomainName\n        <span class=\"token key atrule\">HostedZoneId</span><span class=\"token punctuation\">:</span> Z2FDTNDATAQYW2\n  <span class=\"token key atrule\">RecordAAAA</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">Type</span><span class=\"token punctuation\">:</span> AWS<span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span>Route53<span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span>RecordSet\n    <span class=\"token key atrule\">DependsOn</span><span class=\"token punctuation\">:</span> WebsiteCloudfrontDistribution\n    <span class=\"token key atrule\">Properties</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">HostedZoneId</span><span class=\"token punctuation\">:</span> <span class=\"token tag\">!Ref</span> HostedZone\n      <span class=\"token key atrule\">Name</span><span class=\"token punctuation\">:</span> &lt;yourwebsite<span class=\"token punctuation\">></span>.com\n      <span class=\"token key atrule\">Type</span><span class=\"token punctuation\">:</span> AAAA\n      <span class=\"token key atrule\">AliasTarget</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">DNSName</span><span class=\"token punctuation\">:</span> <span class=\"token tag\">!GetAtt</span> WebsiteCloudfrontDistribution.DomainName\n        <span class=\"token key atrule\">HostedZoneId</span><span class=\"token punctuation\">:</span> Z2FDTNDATAQYW2\n  <span class=\"token key atrule\">RecordWWWA</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">Type</span><span class=\"token punctuation\">:</span> AWS<span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span>Route53<span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span>RecordSet\n    <span class=\"token key atrule\">DependsOn</span><span class=\"token punctuation\">:</span> WebsiteCloudfrontDistribution\n    <span class=\"token key atrule\">Properties</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">HostedZoneId</span><span class=\"token punctuation\">:</span> <span class=\"token tag\">!Ref</span> HostedZone\n      <span class=\"token key atrule\">Name</span><span class=\"token punctuation\">:</span> www.&lt;yourwebsite<span class=\"token punctuation\">></span>.com\n      <span class=\"token key atrule\">Type</span><span class=\"token punctuation\">:</span> A\n      <span class=\"token key atrule\">AliasTarget</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">DNSName</span><span class=\"token punctuation\">:</span> <span class=\"token tag\">!GetAtt</span> WebsiteCloudfrontDistribution.DomainName\n        <span class=\"token key atrule\">HostedZoneId</span><span class=\"token punctuation\">:</span> Z2FDTNDATAQYW2\n  <span class=\"token key atrule\">RecordWWWAAAA</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">Type</span><span class=\"token punctuation\">:</span> AWS<span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span>Route53<span class=\"token punctuation\">:</span><span class=\"token punctuation\">:</span>RecordSet\n    <span class=\"token key atrule\">DependsOn</span><span class=\"token punctuation\">:</span> WebsiteCloudfrontDistribution\n    <span class=\"token key atrule\">Properties</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">HostedZoneId</span><span class=\"token punctuation\">:</span> <span class=\"token tag\">!Ref</span> HostedZone\n      <span class=\"token key atrule\">Name</span><span class=\"token punctuation\">:</span> www.&lt;yourwebsite<span class=\"token punctuation\">></span>.com\n      <span class=\"token key atrule\">Type</span><span class=\"token punctuation\">:</span> AAAA\n      <span class=\"token key atrule\">AliasTarget</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">DNSName</span><span class=\"token punctuation\">:</span> <span class=\"token tag\">!GetAtt</span> WebsiteCloudfrontDistribution.DomainName\n        <span class=\"token key atrule\">HostedZoneId</span><span class=\"token punctuation\">:</span> Z2FDTNDATAQYW2</code></pre></div>\n<h2>Conclusion</h2>\n<p>I’ve collected the templates and functions in this Github repo <a href=\"https://github.com/izifortune/serverless-website-sam\">https://github.com/izifortune/serverless-website-sam</a> ready for\nyou to clone. You can easily build and deploy your serverless website on AWS. If\nyou don’t like working with <em>YAML</em> files or <em>SAM</em> you can have a look at the\n<em>AWS CDK</em> which provides pre-made examples on how to build and deploy a\n<a href=\"https://github.com/aws-samples/aws-cdk-examples/tree/master/typescript/static-site\">static website in AWS</a>.</p>","frontmatter":{"title":"Build a serverless website with SAM on AWS - part 2","date":"April 22, 2020","description":"How to build a serverless website on AWS using SAM cli. Explore Lambda at the Edge to add path rewrites, security headers and assets caching.","socialImage":{"childImageSharp":{"fixed":{"src":"/dist/static/311b4f8931d2415c7fb2c289e5302d42/9f2d5/serverless-website-aws.png"}}},"image":null}}},"pageContext":{"slug":"/serverless-website-sam-aws-part-2/","previous":{"fields":{"slug":"/share-angular-libraries-with-lerna/"},"frontmatter":{"title":"Share Angular libraries with lerna"}},"next":null}}}