Skip to content

Commit 552c6a1

Browse files
committed
CLFURL edits
1 parent 82db0c3 commit 552c6a1

File tree

1 file changed

+11
-10
lines changed

1 file changed

+11
-10
lines changed

src/content/docs/blog/so-long-api-gateway-and-thanks-for-all-the-routes.mdx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ However, most of the time I just need a way to invoke a Lambda Function over HTT
3535
## Why change?
3636

3737
### Cost
38-
Since launch, one of the biggest criticisms of API Gateway has been its pricing. One would expect that Lambda (the thing that's doing the heavy lifting) would make up the majority of the cost, and not the thing that's simply connecting it to a client. In reality, API Gateway often exceeds Lambda costs by more than 2x. Under similar conditions, CloudFront costs ~10% of what Lambda does.
38+
Since launch, one of the biggest criticisms of API Gateway has been its pricing. One would expect that Lambda (the thing that's doing the heavy lifting) would make up the majority of the cost, and not the thing that's simply connecting it to a client. In reality, API Gateway (even the newer, cheaper HTTP API variant) often exceeds Lambda costs by more than 2x. Under similar conditions, CloudFront costs ~10% of what Lambda does.
3939

4040
:::caution[WARNING: Gross oversimplification to prove a point detected]
4141
Saying API Gateway is just a service that connects Lambda over HTTP is unfair. <a href="https://x.com/theburningmonk" target="_blank" rel="noopener">Yan Cui</a> has an excellent comparison of the two approaches in his blog post <a href="https://theburningmonk.com/2024/03/when-to-use-api-gateway-vs-lambda-function-urls/" target="_blank" rel="noopener">When to use API Gateway vs. Lambda Function URLs</a>. He covers many of the advanced features of API Gateway and concludes with a preference for API Gateway in most scenarios.
4242
:::
4343

44-
The one area where API Gateway has a cost advantage is on unauthorized requests. When using the JWT Authorizer, API Gateway eats those costs for you. With CLFURL you would pay both the CloudFront and Lambda costs for unauthorized requests. This is really only a concern when it comes to DDOS (or Denial of Wallet) attacks. Fortunately, <a href="https://docs.aws.amazon.com/waf/latest/developerguide/ddos-event-mitigation-logic-continuous-inspection.html" target="_blank" rel="noopener">AWS Shield Standard</a> (a free DDOS protection service included with CloudFront) *should* mitigate those attacks.
44+
The one area where API Gateway has a cost advantage is on unauthorized requests. When using its Authorizer feature, API Gateway eats those costs for you. With CLFURL you would pay both the CloudFront and Lambda costs for unauthorized requests. This is mostly only a concern when it comes to DDOS (or Denial of Wallet) attacks. Fortunately, <a href="https://docs.aws.amazon.com/waf/latest/developerguide/ddos-event-mitigation-logic-continuous-inspection.html" target="_blank" rel="noopener">AWS Shield Standard</a> (a free DDOS protection service included with CloudFront) *should* mitigate those attacks.
4545

4646
### Max timeout
4747

@@ -67,6 +67,8 @@ In my tests comparing the performance of API Gateway to CloudFront, I found both
6767
1. CloudFront adds a shocking +300ms latency for cross-region (other-side-of-the-world) requests **when not under load**. These findings are why I didn't switch to CLFURL earlier this year. I recently decided to test the performance again, hoping that AWS had resolved the issue. They hadn't. However, after diving deeper I discovered that this latency is akin to a cold start. When under load, this extra latency is a rare occurrence.
6868
2. Under load, CloudFront offers marginally better performance than API Gateway. ~4% faster on average.
6969

70+
Note that these tests were performed against the faster HTTP API flavor of API Gateway. If you're using the original REST API option, you might see a more significant performance difference around the 20% mark.
71+
7072
<figure>
7173
<Image src={apigwLfurlClfurlPerformance} alt="API Gateway vs Lambda Function URL vs CloudFront + LFURL" style={{margin: 'auto'}} />
7274
<figcaption style={{ margin: 0, color: '#666', fontSize: '0.8rem', textAlign: 'center' }}>
@@ -78,12 +80,12 @@ You can try it out yourself at <a href="https://production.d1gtqqp4ixg5qm.amplif
7880

7981
## Locking down the Lambda FURL
8082

81-
If you want to enforce CloudFront security features such as Shield and WAF, you need to ensure attackers can't bypass CloudFront by calling your Lambda Function URL directly. To accomplish this, <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-lambda.html" target="_blank" rel="noopener">AWS recommends</a> setting the LFURL's authorization to AWS_IAM and using OAC to grant access for the distribution. If only it were that simple. Unfortunately, there are two big limitations:
83+
If you want to enforce CloudFront security features such as Shield and WAF, you need to ensure attackers can't bypass CloudFront by calling your Lambda Function URL directly. To accomplish this, <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-lambda.html" target="_blank" rel="noopener">AWS recommends</a> setting the LFURL's authorization to AWS_IAM and using OAC to grant access for the distribution. If only it were that simple. Unfortunately, there are two challenges with this:
8284

83-
1. CloudFront overrides the Authorization header when invoking your Lambda Function, so you need to use a different, non-standard header to include your JWT.
85+
1. CloudFront overrides the `Authorization` header when invoking your Lambda Function, so you need to use a different, non-standard header to include your auth token. You can use CloudFront Functions to copy the original `Authorization` header to a new header like `X-Auth` so that at least your clients can continue using the standard header name.
8486
2. The client needs to sign the payload in PUT/POST methods. See this post by <a href="https://speedrun.nobackspacecrew.com/blog/2024/05/22/using-cloudfront-as-a-lightweight-proxy.html#lambda-oac" target="_blank" rel="noopener">David Behroozi</a> for more details.
8587

86-
A simpler solution proposed by <a href="https://x.com/ryan_sb" target="_blank" rel="noopener">Ryan Scott Brown</a> is to have CloudFront add a custom origin header with a secret: `'X-CloudFront-Secret': 'NoBadGuysAllowed'`. Your Lambda Function can check the secret in the header, and return an error code if it doesn't match. Another solution proposed by <a href="https://x.com/dreamorosi" target="_blank" rel="noopener">Andrea Amorosi</a> uses Lambda@Edge.
88+
A simpler solution proposed by <a href="https://x.com/ryan_sb" target="_blank" rel="noopener">Ryan Scott Brown</a> is to have CloudFront add a custom origin header with a secret: `'X-CloudFront-Secret': 'NoBadGuysAllowed'`. Your Lambda Function can check the secret in the header, and return an error code if it doesn't match. Another solution proposed by <a href="https://x.com/dreamorosi" target="_blank" rel="noopener">Andrea Amorosi</a> uses Lambda@Edge. Since an attacker would need to discover both the Lambda Function URL and the secret before successfully bypassing CloudFront, this solution is likely more than enough for most applications.
8789

8890
<a href="https://x.com/ryan_sb/status/1835363459979960624" target="_blank" rel="noopener">
8991
<figure>
@@ -98,8 +100,6 @@ A simpler solution proposed by <a href="https://x.com/ryan_sb" target="_blank" r
98100

99101
The only "advanced" API Gateway feature I use on all of my APIs is the Cognito/JWT Authorizer. Since CloudFront doesn't have a similar native feature, we need to perform the JWT validation inside the Lambda Function. This has the added bonus of making our app more portable and easier to run locally, while also granting us more flexibility.
100102

101-
Performing JWT validation yourself is also incredibly fast (~4ms cold; ~0.3ms warm in Node.js) thanks to <a href="https://x.com/AWSbrett/status/1779422735539847454" target="_blank" rel="noopener">this tip by David Behroozi</a>.
102-
103103
<figure>
104104
<Image src={jwtExpressMiddleware} alt="Express Middleware for JWT validation" style={{margin: 'auto'}} />
105105
<figcaption style={{ margin: 0, color: '#666', fontSize: '0.8rem', textAlign: 'center' }}>
@@ -114,15 +114,16 @@ Performing JWT validation yourself is also incredibly fast (~4ms cold; ~0.3ms wa
114114
</figcaption>
115115
</figure>
116116

117+
Performing Cognito JWT validation yourself is also incredibly fast (~4ms cold; ~0.3ms warm in Node.js) thanks to <a href="https://x.com/AWSbrett/status/1779422735539847454" target="_blank" rel="noopener">this tip by David Behroozi</a>.
117118

118119
## Conclusion
119120

120121
CloudFront + Lambda Function URL (CLFURL) is an excellent combination for Serverless APIs when you don't need any of the advanced features offered by API Gateway. With CloudFront being an order of magnitude cheaper than API Gateway, it puts it more in line with what'd you'd expect when compared to the cost of other components of your Serverless architecture.
121122

122-
The minor performance improvement is nice, but likely to be unnoticeable to the end user. On the other hand, the increase in the max timeout will surely be appreciated by developers looking to add Generative AI to their API.
123+
The minor performance improvement compared to HTTP API is nice, but likely to be unnoticeable to the end user (compared to REST API may be a different story). On the other hand, the increase in the max timeout will surely be appreciated by developers looking to add Generative AI to their API.
123124

124-
A repo is available at <a href="https://github.com/CodeGenieApp/cloudfront-lambda-url-vs-apigw" target="_blank" rel="noopener">https://github.com/CodeGenieApp/cloudfront-lambda-url-vs-apigw</a>. This repo was generated using <a href="https://app.codegenie.codes/">Code Genie</a> and modified to include CloudFront + Lambda Function URL and benchmarks.
125+
A repo is available at <a href="https://github.com/CodeGenieApp/cloudfront-lambda-url-vs-apigw" target="_blank" rel="noopener">https://github.com/CodeGenieApp/cloudfront-lambda-url-vs-apigw</a>. This repo was generated using <a href="https://app.codegenie.codes/">Code Genie</a> and modified to include CloudFront + Lambda Function URL and benchmarks. Check out the commit history to find the interesting parts.
125126

126127
Code Genie will soon offer CLFURL as an option when generating your source code, and will continue to support API Gateway HTTP APIs as well.
127128

128-
If you've found this blog useful, please give it a reshare! You can follow <a href="https://x.com/AWSbrett" target="_blank" rel="noopener">me</a> and <a href="https://x.com/CodeGenieCodes/" target="_blank" rel="noopener">Code Genie</a> on Twitter. Finally, if you're building something new (or want to refresh something old), be sure to check out Code Genie. It takes care of all the boring parts of starting a new project so you can focus on what's interesting.
129+
If you've found this blog useful, please give it a reshare! You can follow <a href="https://x.com/AWSbrett" target="_blank" rel="noopener">me</a> and <a href="https://x.com/CodeGenieCodes/" target="_blank" rel="noopener">Code Genie</a> on Twitter. Finally, if you're building something new (or want to refresh something old), be sure to check out Code Genie. It takes care of all the boring parts of starting a new project so you can focus on what's interesting!

0 commit comments

Comments
 (0)