<?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>DEV Community: Elif Apaydın</title>
    <description>The latest articles on DEV Community by Elif Apaydın (@elifapaydin).</description>
    <link>https://dev.to/elifapaydin</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%2F854562%2F306e8f23-90e1-4ae1-a657-eea3a5bf6008.jpg</url>
      <title>DEV Community: Elif Apaydın</title>
      <link>https://dev.to/elifapaydin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/elifapaydin"/>
    <language>en</language>
    <item>
      <title>Single Page Application Deployment on AWS</title>
      <dc:creator>Elif Apaydın</dc:creator>
      <pubDate>Sat, 15 Oct 2022 19:35:52 +0000</pubDate>
      <link>https://dev.to/elifapaydin/single-page-application-deployment-on-aws-3o69</link>
      <guid>https://dev.to/elifapaydin/single-page-application-deployment-on-aws-3o69</guid>
      <description>&lt;p&gt;Greetings to all,&lt;/p&gt;

&lt;p&gt;Recently, we all can observe that the popularity of Single Page Applications (SPA), in other words, projects created with technologies such as VueJS, React, and Angular, is increasing in the web world.&lt;/p&gt;

&lt;p&gt;I will explain step by step how we can deploy our SPA projects on Amazon Web Services (AWS).&lt;/p&gt;

&lt;p&gt;In my article, I will get on with the demo project I had prepared with Vue.&lt;/p&gt;

&lt;p&gt;What kind of structure would be better for us to set up regarding the performance and security of our application? Which AWS can we use for it? Together, we'll explore the answers to these questions.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Amazon Web Services to use:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Certificate Manager&lt;/li&gt;
&lt;li&gt;S3&lt;/li&gt;
&lt;li&gt;CloudFront&lt;/li&gt;
&lt;li&gt;Route53&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AWS that we're going to use will be Certificate Manager, S3, CloudFront, and Route53.&lt;/p&gt;

&lt;p&gt;AWS that we're going to use will be Certificate Manager, S3, CloudFront, and Route53.&lt;/p&gt;

&lt;p&gt;We will create an SSL certificate with Certificate Manager (ACM) to provide the HTTPS connection.&lt;/p&gt;

&lt;p&gt;We will use the S3 service to store the content of our website, that is, to work as Object Storage.&lt;/p&gt;

&lt;p&gt;We will use CloudFront for the CDN service and, finally, Route53 for our DNS redirects.&lt;/p&gt;

&lt;p&gt;Let's take a quick look at these services 🚀&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;AWS Certificate Manager&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS Certificate Manager (ACM) is a service that we can use to create, manage, and distribute SSL/TLS certificates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S7MgcKZe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mhztx1pxrjt5z6dbq9m0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S7MgcKZe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mhztx1pxrjt5z6dbq9m0.png" alt="ACM" width="104" height="104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this service, we can easily perform certificate renewal transactions automatically.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;AWS S3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS S3 is an object storage service that offers data availability and performance capabilities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7X4d4Vz6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wzt53jcgljco34uktbk0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7X4d4Vz6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wzt53jcgljco34uktbk0.png" alt="S3" width="104" height="104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using this service, we can store data for various usage scenarios such as repository, web pages, mobile applications, backup and restore, and archiving.&lt;br&gt;
S3 is the cheapest object storage option. For example, in Frankfurt, you pay a monthly fee of $1.2 for 50TB of data you store on a bucket.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;AWS CloudFront&lt;/strong&gt;&lt;br&gt;
By using a global network of edge locations, CloudFront provides CDN service for us.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Blv1lTaV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ezor4rum2iafsgc6lg9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Blv1lTaV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ezor4rum2iafsgc6lg9.png" alt="Image description" width="104" height="104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In other words, the content of our website is cached on Amazon servers in different locations around the world, and each request to our website by the end user is responded to from the servers in the region closest to the end user, thanks to CloudFront.&lt;br&gt;
In this way, CloudFront helps us to lower our costs as it minimizes response time and maximizes bandwidth speed.&lt;br&gt;
CloudFront also allows us to set up security issues such as blocking users in certain locations from accessing content and requiring the user to access content using CloudFront URLs and protects against DDoS attacks.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;AWS Route53&lt;/strong&gt;&lt;br&gt;
AWS Route53 can be used both for domain purchase and to manage the domain we purchased from a different place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9ugIaXVY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0qaqfniupb04fgk67ja2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9ugIaXVY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0qaqfniupb04fgk67ja2.png" alt="Route53" width="104" height="104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each domain that we keep on Route53 is called Hosted Zone.&lt;br&gt;
We can route a domain we have purchased to the IP or CNAME address we want by creating DNS records such as A record, CNAME, and MX within this service.&lt;br&gt;
How about pricing for Route53?&lt;br&gt;
For the first 25 hosted zones you create in your account, you will be charged $0.50 per month per hosted zone. When you start to keep more than 25 zones, you will only be charged $0.10 per zone.&lt;br&gt;
Pricing per inquiry is charged according to the Routing Policy you use.&lt;br&gt;
Standard inquiries are charged $0.40 for every million inquiries for the first one billion inquiries per month and $0.20 for one million queries for those over 1 billion.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;How will we use these services we have mentioned?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DD6gkyG1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/613k6xvm24wq0mgdok5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DD6gkyG1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/613k6xvm24wq0mgdok5u.png" alt="Image description" width="880" height="699"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we examine the structure we will create through this diagram:&lt;/p&gt;

&lt;p&gt;Here, the traffic from the end user will first reach Route53, enter DNS resolution, and from there to CloudFront.&lt;/p&gt;

&lt;p&gt;The CloudFront CDN service will ensure that the content of our website in our S3 bucket reaches the end user from the cache.&lt;/p&gt;

&lt;p&gt;In this structure, our SSL certificate, which we created using ACM for a secure connection, will be activated and will play a role in establishing the HTTPS connection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2_dY_crY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/67d3pnjb4ebhi3n4ixb4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2_dY_crY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/67d3pnjb4ebhi3n4ixb4.png" alt="Diagram" width="880" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we consider different scenarios,&lt;/p&gt;

&lt;p&gt;Of course, instead of CloudFront, we could've used our own servers that we would've rented and configured the CDN service ourselves. However, when the traffic increases on the site, we need to rent more servers and deal with the management of these servers. This, in return, will be reflected as effort and cost.&lt;/p&gt;

&lt;p&gt;We can create the same structure without CloudFront but using only S3. For this, it will be enough to enable Static Website Hosting from the Properties of the S3 bucket.&lt;/p&gt;

&lt;p&gt;However, each request that a website hosted on S3 will create an additional cost. This method might be too much expensive for a high-traffic website. Therefore, using CloudFront within the structure can ensure that our website is more efficient with less cost.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Things to do:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JFYZ0k1X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ltmmn9ss48a50wvysy2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JFYZ0k1X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ltmmn9ss48a50wvysy2.png" alt="Image description" width="528" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I. Creating an IAM user on AWS for the Bitbucket deployment step.&lt;br&gt;
II. Creating an SSL certificate for the HTTPS connection.&lt;br&gt;
III. Creating an S3 bucket.&lt;br&gt;
IV. Creating a CloudFront CDN.&lt;br&gt;
V. DNS forwarding via Route53.&lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;Creating an AWS IAM user&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yAn4v4Eh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mdg31v7c6t880p6yauq7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yAn4v4Eh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mdg31v7c6t880p6yauq7.png" alt="Image description" width="478" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On AWS IAM, we click Add Users and create a user and select Programmatic Access to get the Access Key ID and Secret Access Key information that we will use to deploy our project over Bitbucket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MUG50_Uz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0242nhp3lnweu9oplopw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MUG50_Uz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0242nhp3lnweu9oplopw.png" alt="Creating IAM on AWS" width="880" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We need to create and assign two policies so that the user we have created can perform deployment and cache clear operations as in the example.&lt;/p&gt;

&lt;p&gt;We ensure access security by typing the name of the S3 bucket that we will create in these policies and specifying the IP addresses published by Bitbucket in a source IP condition. Thanks to this authorization, we will be able to operate Get, List, Put and Delete Object on our bucket.&lt;/p&gt;

&lt;p&gt;Bitbucket-Deployment-Policy to upload the files of our project to the S3 bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "S3BucketPolicy",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:ListBucket",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::BUCKET_NAME",
                "arn:aws:s3:::BUCKET_NAME/*"
            ],
            "Condition": {
                "ForAnyValue:IpAddress": {
                    "aws:SourceIp": [
                        "34.199.54.113/32",
                        "34.232.25.90/32",
                        "34.232.119.183/32",
                        "..."
                    ]
                }
            }
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we look at CloudFront-Cache-Clear-Policy, here we authorize to create of Invalidation in the CF Distribution we will create.&lt;/p&gt;

&lt;p&gt;Invalidation will be required to make our cache clear in our CDN service after the deployment of the project, which we will use in the pipeline step.&lt;/p&gt;

&lt;p&gt;CloudFront-Cache-Clear-Policy;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "CloudFrontCacheInvalidationPolicy",
            "Effect": "Allow",
            "Action": "cloudfront:CreateInvalidation",
            "Resource": "arn:aws:cloudfront::ACCOUNT_ID:distribution/CF_DISTRIBUTION_ID",
            "Condition": {
                "ForAnyValue:IpAddress": {
                    "aws:SourceIp": [
                        "34.199.54.113/32",
                        "34.232.25.90/32",
                        "34.232.119.183/32",
                        "..." 
                    ]
                }
            }
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Creating SSL certificate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k7WPEEEk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/27jcjdg793899lyfabxg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k7WPEEEk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/27jcjdg793899lyfabxg.png" alt="Image description" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On AWS Certificate Manager, we need to click on the Request a Certificate button from Certificates and request an SSL certificate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V1hED15j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2smpb39hoof4fbfn3f9j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V1hED15j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2smpb39hoof4fbfn3f9j.png" alt="Image description" width="880" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the screen that pops up, we click on "Request a public certificate" and proceed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eaD-_iqz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/td3s41wmlco50wqntld1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eaD-_iqz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/td3s41wmlco50wqntld1.png" alt="Image description" width="880" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After that, we specify the FQDN, namely the Domain Name we'd like to use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--070-CNGY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jqnu6l6pcitz914qts38.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--070-CNGY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jqnu6l6pcitz914qts38.png" alt="Image description" width="880" height="653"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For SSL, we must choose a validation method. Here AWS gives us two options.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;DNS validation&lt;/em&gt; is the recommended method.&lt;/p&gt;

&lt;p&gt;If we choose it, we will verify our SSL certificate by adding CNAME records to our Hosted Zone on Route53.&lt;/p&gt;

&lt;p&gt;Our other validation method is,&lt;/p&gt;

&lt;p&gt;Email validation, we send a verification email to the mail account authorized for the domain. We can verify our SSL certificate with it.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Creating S3 Bucket&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M1ZLh_9---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tf5ogzz84hrzb5v9fllr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M1ZLh_9---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tf5ogzz84hrzb5v9fllr.png" alt="Image description" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we choose S3 on AWS Console.&lt;/p&gt;

&lt;p&gt;On this screen, we can list our existing buckets by clicking on Buckets from the menu on the left, and in the same way, we can choose Create Bucket on the right and start creating a new one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5AkAq10r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v1lkqw4c92vho6e9tpea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5AkAq10r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v1lkqw4c92vho6e9tpea.png" alt="Image description" width="880" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After proceeding to the bucket creation step, we can make the general settings of our bucket here.&lt;/p&gt;

&lt;p&gt;First, we will name our bucket and then choose which region we want it to be in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yQQVPAVx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q590f4wdswf23xlwsy0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yQQVPAVx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q590f4wdswf23xlwsy0l.png" alt="Image description" width="880" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If there is a bucket we had created before, we can create a new bucket with the same settings of that bucket by choosing "Copy settings from the existing bucket".&lt;/p&gt;

&lt;p&gt;By default, in the S3 buckets we create, an S3 object belongs to the AWS account that uploaded it, and we can change it with the ACL, that is, the Access Control List, from the Object Ownership screen when creating the S3 bucket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gmi7fW2p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d979z5pmdepsbdtgtqhk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gmi7fW2p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d979z5pmdepsbdtgtqhk.png" alt="Image description" width="880" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Disabling ACL is recommended here.&lt;/p&gt;

&lt;p&gt;ACLs are not recommended, except for unusual situations where you need to control access for each object separately.&lt;/p&gt;

&lt;p&gt;Instead, we must grant permissions using bucket policies. We will continue by choosing recommended one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QgAaQTEx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sa537645yx60xjl3p2d7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QgAaQTEx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sa537645yx60xjl3p2d7.png" alt="Image description" width="880" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this section, we see the restricted options regarding the accessibility of the bucket we created. Here we will proceed by closing the public access to the bucket.&lt;/p&gt;

&lt;p&gt;Because if our bucket URL exposes and receives many requests from malicious ones, there will be additional costs for us.&lt;/p&gt;

&lt;p&gt;In the following steps, we will ensure that only CF, which we will use as a CDN, can access the bucket, and in this way, we will reduce our S3 cost.&lt;/p&gt;

&lt;p&gt;At this stage, we can manually upload the build or static HTML files of our project into the bucket we created by clicking on Add files or Add Folder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tjLMyaVV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xdtgxo8guc8vps66c54b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tjLMyaVV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xdtgxo8guc8vps66c54b.png" alt="Image description" width="880" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But instead of manually uploading the file, we will continue by showing how we can deploy our project with Bitbucket Pipeline.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Bitbucket Pipeline&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RfO4j0bE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sopfln988s3q9l2rsrsy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RfO4j0bE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sopfln988s3q9l2rsrsy.png" alt="Image description" width="400" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We received the Access Key ID and Secret Access Key information for the user that we created in the AWS IAM creating a user step, which was mentioned at the beginning of the article.&lt;/p&gt;

&lt;p&gt;To upload the build files of our project to our S3 bucket with Bitbucket Pipeline, we need to define these keys as variables from the Repository Variables section by selecting the project's Repository settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QuyYI3s5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/omkbuy3bv15fg4i7t50a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QuyYI3s5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/omkbuy3bv15fg4i7t50a.png" alt="Image description" width="880" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First of all, if we take a look at the folder structure of our project, our bitbucket-pipelines.yml file should be in the main directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hWBexnD9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4gokn43y8oecu1ptivbt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hWBexnD9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4gokn43y8oecu1ptivbt.png" alt="Image description" width="502" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On Pipeline, we can see the "Build &amp;amp; Deploy to S3" step that will be applied if a commit is sent to Master Branch in the example given. In this step, we first run "npm install" in the runtime mode and then get it to build in the production mode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--df7ftFqR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/diua79l6bmtv2iqe8y11.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--df7ftFqR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/diua79l6bmtv2iqe8y11.png" alt="Image description" width="880" height="797"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Afterward, we deploy the dist folder containing our build files to S3, using the keys of the IAM user for which we have given the S3 and CF authority that we have created for the deployment. Here we need to write the region code and the bucket name.&lt;/p&gt;

&lt;p&gt;After the deployment, we need to clear the cache for the changes made to be active on CloudFront.&lt;/p&gt;

&lt;p&gt;We perform this by typing our CF Distribution ID in the second pipe section of the pipeline.&lt;/p&gt;

&lt;p&gt;After we prepare our Pipeline in this way, we can run it. 🚀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ucLH2GvS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gauur7gxyj8rgjeo3zzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ucLH2GvS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gauur7gxyj8rgjeo3zzo.png" alt="Image description" width="880" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we take a look at our S3 bucket after the successful pipeline step, we can see that the build files of our project are there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q0RHP_Oe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ivfqy0yx48arhm4c6vbr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q0RHP_Oe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ivfqy0yx48arhm4c6vbr.png" alt="Image description" width="880" height="694"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Creating a CloudFront Distribution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2_OfCpmW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/57uttgu9pnvywgt8ljrv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2_OfCpmW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/57uttgu9pnvywgt8ljrv.png" alt="Image description" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We click on CloudFront, which we will use as a CDN service, and list the current distributions or create a new distribution from Distributions on the left side of the menu.&lt;/p&gt;

&lt;p&gt;Or, we can directly choose "Create a CloudFront Distribution" and proceed to the creating step from the screen below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TfcAkqYP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zq5qb7csm0h1lu8vjj3r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TfcAkqYP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zq5qb7csm0h1lu8vjj3r.png" alt="Image description" width="880" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to create a Distribution, we first need to fill out Origin Domain. That is, we will choose what distribution will use as a source.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3IzTMOGV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rjfffpm26vb0r8hq6xux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3IzTMOGV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rjfffpm26vb0r8hq6xux.png" alt="Image description" width="880" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we select the bucket named demo.techses.net, which is the S3 bucket that we created before, and specify it as a source.&lt;/p&gt;

&lt;p&gt;If we want, we can specify a path in this S3 separately. It will be named as the selected resource.&lt;/p&gt;

&lt;p&gt;When we created our S3 bucket, we hadn't allowed it public access. That's why we're now about to create an Origin Access Identity (OAI) for the CloudFront Distribution to access the S3 bucket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--idliyewQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z905j6nrtptrjh0agyff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--idliyewQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z905j6nrtptrjh0agyff.png" alt="Image description" width="880" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this identity to access the S3 bucket and the objects in the bucket, we need a Bucket Policy with GetObject permission.&lt;/p&gt;

&lt;p&gt;For this policy to come up automatically, in the Bucket Policy section on the screen we are on, we continue by selecting, Yes, update the bucket policy and ensure that the policy is defined simultaneously while completing all stages of creating a CDN.&lt;/p&gt;

&lt;p&gt;Here, we can request CloudFront to respond to incoming traffic HTTP and HTTPS requests separately, redirect HTTP requests to HTTPS, or respond only to HTTPS requests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L9DpNJ5v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ipcnhfaot45yrvvp3xlq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L9DpNJ5v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ipcnhfaot45yrvvp3xlq.png" alt="Image description" width="880" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we will ensure that incoming HTTP traffic is redirected to HTTPS for security reasons.&lt;/p&gt;

&lt;p&gt;Again, on the same screen, we can make a restriction by choosing which HTTP methods we allow. For example, we can prevent the end user from sending a PUT or DELETE request.&lt;/p&gt;

&lt;p&gt;Here we can choose which edge locations we want to use from Price Class for our CDN.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JfNTeN2j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1vhekp0xjojxa00q94b5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JfNTeN2j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1vhekp0xjojxa00q94b5.png" alt="Image description" width="880" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can make a choice according to the location of our S3 bucket; we will continue by choosing all locations for the best performance.&lt;/p&gt;

&lt;p&gt;If we want to restrict access to our website or define special firewall rules, we can activate access rules by selecting web ACL with optional AWS WAF.&lt;/p&gt;

&lt;p&gt;Here we can enter the URL we want to use to access our CDN as an Alternate Domain Name. This step will allow us to add an Alias Record while forwarding DNS on Route53.&lt;/p&gt;

&lt;p&gt;Here we will select the SSL certificate that we created on ACM, and we will ensure that our CDN offers a secure connection with HTTPS; we can choose our TLS version in the Security Policy section; we will continue with recommended TLS version here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mDUVUsqy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vsz27i4zitdozj76id3n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mDUVUsqy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vsz27i4zitdozj76id3n.png" alt="Image description" width="880" height="695"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We set up our CDN. Here we can have a look at our current configuration and edit them later if we wish.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UVLqN39b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1fg4bi4x2ahxns54t5ze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UVLqN39b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1fg4bi4x2ahxns54t5ze.png" alt="Image description" width="880" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On your left, number 1 gives you the DistributionID information.&lt;/p&gt;

&lt;p&gt;By specifying this ID in the second pipe section of the pipeline, we use it to clear the cache automatically on CDN after the deployment.&lt;/p&gt;

&lt;p&gt;However, if we wish to do this manually, we select the Invalidation tab, which is signed as number 2 on the CloudFront Distribution screen, and specify the directory in which we want to clear the cache or if we want to have the cache removed in all our files, we can complete it by specifying as /*.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Route53 DNS rounting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mt-JBOwE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vblhna5cwr9g92fos6h1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mt-JBOwE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vblhna5cwr9g92fos6h1.png" alt="Image description" width="499" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have created our resources and prepared our infrastructure with ACM, S3, and CloudFront, and now it's time for DNS routing.&lt;/p&gt;

&lt;p&gt;For this, we need to select Route53 from AWS Console and click on Hosted Zone, where our domains are, and enter the domain that we will use for our website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rSHMn3G4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yw7tkpt57get3tq30983.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rSHMn3G4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yw7tkpt57get3tq30983.png" alt="Image description" width="880" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We start creating our DNS record by selecting Create Record on the Domain screen.&lt;/p&gt;

&lt;p&gt;Here, we continue by entering Record name and choosing Record type we will make.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--paWUvoQE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfhkfey2c931fori3162.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--paWUvoQE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfhkfey2c931fori3162.png" alt="Image description" width="880" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We need to create an A record for the DNS record, which we will route to our website. Since we usually make an A record, we must enter the IP address to which we will monitor the traffic.&lt;/p&gt;

&lt;p&gt;However, AWS offers us the option of Alias Record as a method we can use for its services.&lt;/p&gt;

&lt;p&gt;By choosing Alias Record, we determine which service the source to which we will monitor the traffic is working and which region it is in.&lt;/p&gt;

&lt;p&gt;In other words, we choose CloudFront and Virginia, the region where we use this service.&lt;/p&gt;

&lt;p&gt;Then, we can see the list of CloudFront distributions in this region and select the distribution we created for our website from here.&lt;/p&gt;

&lt;p&gt;A minor detail here; there was the Alternate Domain Name part that we specified when creating our CDN, and we entered the URL that we wanted to use for the website.&lt;/p&gt;

&lt;p&gt;The benefit of doing this is that:&lt;/p&gt;

&lt;p&gt;It matches the Record Name we entered while forwarding and only lists the CDN. We directly select the relevant CDN and create our DNS record with the Simple Routing Policy.&lt;/p&gt;

&lt;p&gt;And voila! As you can see, our website is ready. ☺️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qRLqreiO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zv9kwmlxciiwwnsvhqds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qRLqreiO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zv9kwmlxciiwwnsvhqds.png" alt="Image description" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;You can find the demo project and &lt;a href="https://github.com/elif-apaydin/vue-demo-spa/blob/main/bitbucket-pipelines.yml"&gt;bitbucket-pipelines.yml&lt;/a&gt; file I used for my article from my &lt;a href="https://github.com/elif-apaydin/vue-demo-spa"&gt;Github&lt;/a&gt; repo :)&lt;/p&gt;

&lt;p&gt;You can watch the &lt;a href="https://www.youtube.com/watch?v=98HFOTInPdk"&gt;Youtube&lt;/a&gt; stream here. For your questions, you can reach me on my &lt;a href="https://twitter.com/elifapaydin_"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/elifapaydin/"&gt;LinkedIn&lt;/a&gt; accounts.&lt;/p&gt;

&lt;p&gt;Thank you for reading. I hope it has been helpful 🙌&lt;/p&gt;

</description>
      <category>aws</category>
      <category>singlepageapplication</category>
      <category>spa</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Fine Details of AWS Lambda Function URL Feature</title>
      <dc:creator>Elif Apaydın</dc:creator>
      <pubDate>Wed, 27 Apr 2022 22:16:20 +0000</pubDate>
      <link>https://dev.to/elifapaydin/fine-details-of-aws-lambda-function-url-feature-21kn</link>
      <guid>https://dev.to/elifapaydin/fine-details-of-aws-lambda-function-url-feature-21kn</guid>
      <description>&lt;p&gt;As you know, it recently announced that it has brought Function URL generation support for AWS Lambda service. With the Function URL property, there is now a new way to call a Lambda Function via HTTP API call.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c7ZJMDlF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3mayok1kifu4h7uin6g2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c7ZJMDlF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3mayok1kifu4h7uin6g2.png" alt="Image description" width="720" height="263"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;This feature is actually not new, previously we had to integrate Lambda with API Gateway (v1 or v2) or Application Load Balancer to call a Function via HTTP request. API Gateway had a free tier, but after that free tier you would be charged a $1/million request fee, not including the time required for Lambda Function to run.&lt;/p&gt;




&lt;p&gt;The main difference between API Gateway and Lambda Function URL;&lt;/p&gt;

&lt;p&gt;Function URL is a FREE way to call Lambda Function via HTTP method. You only pay for the very small additional uptime that occurs by serializing the request and response.&lt;/p&gt;

&lt;p&gt;Lambda Function URL is built into Lambda Function itself. So you don't need to configure an external API Gateway (v1)or HTTP API (v2).&lt;/p&gt;

&lt;p&gt;You can generate a Function URL through the AWS console by creating a new Function or editing an existing Function.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IWGmjTRq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/alx1jdv7jompwagt64a4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IWGmjTRq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/alx1jdv7jompwagt64a4.gif" alt="Image description" width="600" height="338"&gt;&lt;/a&gt; &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Lambda URL - Timeout in seconds&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rest API = 29&lt;/li&gt;
&lt;li&gt;HTTP API = 30&lt;/li&gt;
&lt;li&gt;Lambda URL = 900 (15min)&lt;/li&gt;
&lt;li&gt;CloudFront proxying to Lambda URL = 60 (by default)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Lambda Function Authentication Types&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Authentication types are limited to Public or IAM authority.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sI0Q1oE5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yyjiub1cdfdzbc4mj6dj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sI0Q1oE5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yyjiub1cdfdzbc4mj6dj.png" alt="Image description" width="630" height="422"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS_IAM&lt;/strong&gt; -Lambda uses AWS Identity and Access Management (IAM) to authenticate and authorize requests based on the identity policy of the IAM principal and the resource-based policy of the function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should use this option if you only want authenticated IAM users and roles to call your Funtion with the Funtion URL.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NONE&lt;/strong&gt;-This authentication type does not authenticate any requests to your Lambda Function. So any user can call your Lambda Function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In some cases, you may want your Function URL to be public.&lt;/p&gt;

&lt;p&gt;For example, you may want to respond to requests made directly from a Web browser. In this case you should choose NONE authentication type to allow public access to your Function URL.&lt;/p&gt;

&lt;p&gt;Function URLs are 32 characters randomly and are not easily guessed. So the risk of someone finding it's very small. They can only access if they know the URL and if AuthType=NONE.&lt;/p&gt;

&lt;p&gt;AWS Lambda Function URL have this format;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://&amp;lt;url-id&amp;gt;.lambda-url.&amp;lt;region&amp;gt;.on.aws&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Not guessing the URL but a leak or accidentally making the URL public can be a problem.&lt;/p&gt;

&lt;p&gt;So, what action can you take for such situations?&lt;/p&gt;

&lt;p&gt;You can use the following Service Control Policy (SCP) to prevent users from creating or updating Function URLs that use a method other than the AWS_IAM authentication type:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OBJay_bB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xbwa1ul9ivz24anksz3c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OBJay_bB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xbwa1ul9ivz24anksz3c.png" alt="Image description" width="720" height="376"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;You should review this document for sample SCPs.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Configure CORS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can allow access to any specific domain with the Function URL. When you enable CORS configuration for your Lambda Function URL, the following settings become available.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nIJPkcYO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uqa8jv72g3k98mdxbbxu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nIJPkcYO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uqa8jv72g3k98mdxbbxu.png" alt="Image description" width="793" height="257"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;You can also configure certain titles to be allowed or displayed as array list items.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vq9vCyjf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rykj8ipo1szofawhq7wm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vq9vCyjf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rykj8ipo1szofawhq7wm.png" alt="Image description" width="789" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since Lambda Function requests are made over the HTTP method, we can also ensure that only the required HTTP method is allowed for the request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wtxwpB86--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06jttcsqoutv4h0xdw3h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wtxwpB86--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06jttcsqoutv4h0xdw3h.png" alt="Image description" width="772" height="126"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Request allows cookies of credentials to be stored and we can also specify the maximum time for cached requests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4b--LREt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lnx65v2w13ozas86sko3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4b--LREt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lnx65v2w13ozas86sko3.png" alt="Image description" width="787" height="175"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Lambda URL Use Cases&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service to service integrations&lt;/li&gt;
&lt;li&gt;Webhooks&lt;/li&gt;
&lt;li&gt;Lambda function serving as Mono-Lambda function&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Lambda URL Pricing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Function URLs are included in Lambda's request and duration pricing, so you don't pay extra. In this respect it is the cheapest way to call Lambda Function outside of the AWS cloud.&lt;/p&gt;

&lt;p&gt;For example, let's say you deploy a single Lambda function in the Virginia region with 128 MB of memory and an average call time of 50 ms. The function receives 5 million requests each month; so the cost for requests is $1 and the duration is $0.53, so the grand total will be $1.53 per month.&lt;/p&gt;

&lt;p&gt;Lambda URL - Pricing per million requests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rest API (first 333 mil) = $3.5&lt;/li&gt;
&lt;li&gt;HTTP API (first 300 mil) = $1.0&lt;/li&gt;
&lt;li&gt;Lambda URL = Free&lt;/li&gt;
&lt;li&gt;CloudFront proxying to Lambda URL = ~ $1.0 to $1.2&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Differences Between AWS Lambda Function URLs &amp;amp; Amazon API Gateway&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If we look at the differences between Function URL and API Gateway in general;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w1ngaEwX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fpepl3xxjfokhaot2bwl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w1ngaEwX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fpepl3xxjfokhaot2bwl.png" alt="Image description" width="690" height="1192"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this article you've been given an initial idea about AWS Lambda Function URLs so that you are able to explore it and use it in production scenarios later.&lt;/p&gt;

&lt;p&gt;AWS Lambda Function URLs should work well for use cases like APIs built with backend frameworks, webhooks, form validators, server-side website rendering, long-polling scenarios, single-function simple micro-service, and other relevant cases.&lt;/p&gt;

&lt;p&gt;Amazon API Gateway should work well for advanced use cases like SaaS applications that need to track limit usage using API Gateway Usage Plans, real-time applications using WebSockets, cases where API response time is within 29 seconds, cases that require advanced authorization using AWS Cognito, Throttling, Caching, Service Proxy to other AWS services, and more.&lt;/p&gt;




&lt;p&gt;Thank you for reading. I hope it was useful.&lt;br&gt;
If you like this blog post, please like and share it to reach more people :)&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>lambda</category>
    </item>
  </channel>
</rss>
