I have a simple Lambda function that is fronted by CloudFront. This Lambda also has a function URL.
Invoking the Lambda using the Function URL is way faster than invoking it via CloudFront. Based on my understanding of CloudFront, I’d have expected it to be much quicker.
I’ve created a small CloudFormation template to help reproduce this behaviour.
AWSTemplateFormatVersion: '2010-09-09'
Resources:
LambdaExecutionRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: 'lambda.amazonaws.com'
Action: 'sts:AssumeRole'
Policies:
- PolicyName: 'LambdaExecutionPolicy'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: 'logs:*'
Resource: '*'
HelloWorldFunction:
Type: 'AWS::Lambda::Function'
Properties:
FunctionName: 'HelloWorldFunction'
Runtime: 'nodejs18.x'
Architectures: ['arm64']
Handler: 'index.handler'
Role: !GetAtt LambdaExecutionRole.Arn
Code:
ZipFile: |
exports.handler = async (event) => {
return {
statusCode: 200,
body: 'Hello, World!',
};
};
FunctionUrl:
Type: 'AWS::Lambda::Url'
Properties:
AuthType: NONE
TargetFunctionArn: !GetAtt HelloWorldFunction.Arn
LambdaPermission:
Type: 'AWS::Lambda::Permission'
Properties:
Action: 'lambda:InvokeFunctionUrl'
FunctionName: !GetAtt HelloWorldFunction.Arn
Principal: '*'
FunctionUrlAuthType: 'NONE'
CloudFrontDistribution:
Type: 'AWS::CloudFront::Distribution'
Properties:
DistributionConfig:
Enabled: true
DefaultRootObject: ''
Origins:
- Id: 'LambdaOrigin'
DomainName: !Select [2, !Split ['/', !GetAtt FunctionUrl.FunctionUrl]]
CustomOriginConfig:
HTTPPort: 443
HTTPSPort: 443
OriginProtocolPolicy: 'https-only'
DefaultCacheBehavior:
TargetOriginId: 'LambdaOrigin'
ViewerProtocolPolicy: 'redirect-to-https'
AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
ForwardedValues:
QueryString: false
Cookies:
Forward: none
CachePolicyId: '4135ea2d-6df8-44a3-9df3-4b5a84be39ad' # Managed policy for caching disabled
OriginRequestPolicyId: 'b689b0a8-53d0-40ab-baf2-68738e2966ac' # Managed policy for all viewer except host header
ViewerCertificate:
CloudFrontDefaultCertificate: true
Outputs:
FunctionUrl:
Description: 'URL of the Lambda function'
Value: !GetAtt FunctionUrl.FunctionUrl
Export:
Name: 'FunctionUrl'
CloudFrontUrl:
Description: 'URL of the CloudFront distribution'
Value: !Sub 'https://${CloudFrontDistribution.DomainName}'
Export:
Name: 'CloudFrontUrl'
There are no caches, no VPC-based resources, no cross-region gotchas – this is as simple as it gets.
Here’s a screenshot from Chrome where you can see the CloudFront requests taking over a second, while the Lambda URL ones, being one-third of that.
What could be the reason that the function URL outperforms the CloudFront distribution?