Published on

Guide to AWS Lambda Function URLs

Authors
Tired of constantly switching between AWS Console tabs? 🤕
Time to boost your productivity with Cloudash — an AWS desktop client.

For the longest time, the go-to way of calling an AWS Lambda function via HTTP(S) was to put an API Gateway in front of it. While this works well (and it's still a required pattern for many, many serverless use cases), sometimes developers wanted to "just call an AWS Lambda function" by sending a network request.

Introducing AWS Lambda Function URLs - a new feature in AWS Lambda that allows you to call a Lambda function without an API Gateway.

What is a Lambda Function URL?

Simply put - it's an URL that you can send a network request to to call a Lambda function. An example URL may look like this:

https://vg7sczk62ycloct63yi3wjpoj40sgkuz.lambda-url.eu-central-1.on.aws/

or, in general:

https://<url-id>.lambda-url.<region>.on.aws/

Lambda function URL can point to either $LATEST or to an user-defined alias. Even though no additional resources are required (e.g. API Gateway or an ALB), function URL is a separate CloudFormation resource which needs to be provisioned separately.

How do I create an AWS Lambda function URL?

You can create an AWS Lambda function URL via:

  • AWS Management Console (Cloudash team does not recommend so-called ClickOps for production usage, use IaC instead)
  • AWS CLI
  • AWS CloudFormation
  • AWS Serverless Application Model (AWS SAM)
  • AWS CDK

Could you give me an example?

Sure thing, this is how one might create an AWS Lambda function + and a function url to call it directly using AWS CDK:

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";

export class LambdaUrlStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const myLambda = new cdk.aws_lambda.Function(this, "myLambda", {
      code: cdk.aws_lambda.Code.fromAsset("lambda"),
      handler: "index.handler",
      runtime: cdk.aws_lambda.Runtime.NODEJS_16_X,
    });

    const lambdaUrl = new cdk.aws_lambda.CfnUrl(this, "lambdaUrl", {
      targetFunctionArn: myLambda.functionArn,
      authType: cdk.aws_lambda.FunctionUrlAuthType.NONE,
    });
  }
}

And yes, authType: cdk.aws_lambda.FunctionUrlAuthType.NONE is not exactly secure. Keep reading to learn more:

Security and auth model for Lambda function URLs

Controlling access to Lambda function URLs is performed using the AuthType parameter combined with resource-based policies attached to a particular function.

To quote AWS docs, there are two possible values of AuthType parameter:

  • AWS_IAM – Lambda uses AWS Identity and Access Management (IAM) to authenticate and authorize requests based on the IAM principal's identity policy and the function's resource-based policy. Choose this option if you want only authenticated IAM users and roles to invoke your function via the function URL.
  • NONE – Lambda doesn't perform any authentication before invoking your function. However, your function's resource-based policy is always in effect and must grant public access before your function URL can receive requests. Choose this option to allow public, unauthenticated access to your function URL.

AWS_IAM auth type

When a Lambda function URL has an AWS_IAM AuthType then whoever needs to invoke this particular Lambda function URL must have the lambda:InvokeFunctionUrl permission for a particular Lambda function.

Note: lambda:InvokeFunctionUrl is different than lambda:InvokeFunction, so you may need to adjust your IAM permissions accordingly when opting in to use this feature.

NONE auth type

When a Lambda function URL AuthType is set to NONE then any unauthenticated user with your function URL can invoke your function - proceed with caution. This might be useful whenever you might want to allow public access to your function URL e.g. via a web browser.

Even though the auth type is set to NONE and Lambda does not use IAM to authenticate requests to a function URL, whoever tries to the function needs to have the lambda:InvokeFunctionUrl permissions.

An example function URL invoke policy for all unauthenticated principals:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "lambda:InvokeFunctionUrl",
            "Resource": "arn:aws:lambda:us-east-1:123456789012:function:my-function",
            "Condition": {
                "StringEquals": {
                    "lambda:FunctionUrlAuthType": "NONE"
                }
            }
        }
    ]
}

Source

When creating a function URL with auth type set to NONE via the AWS Console or AWS SAM, the resource-based policy statement listed above will be automatically created for you. When e.g. using AWS SDK you'd need to create it yourself, example of how one might want to do this below:

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";

export class LambdaUrlStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const myLambda = new cdk.aws_lambda.Function(this, "myLambda", {
      code: cdk.aws_lambda.Code.fromAsset("lambda"),
      handler: "index.handler",
      runtime: cdk.aws_lambda.Runtime.NODEJS_16_X,
    });

    const lambdaUrl = new cdk.aws_lambda.CfnUrl(this, "lambdaUrl", {
      targetFunctionArn: myLambda.functionArn,
      authType: cdk.aws_lambda.FunctionUrlAuthType.NONE,
    });

    const lambdaPermission = new cdk.CfnResource(this, "lambdaPermission", {
      type: "AWS::Lambda::Permission",
      properties: {
        Action: "lambda:InvokeFunctionUrl",
        FunctionName: myLambda.functionArn,
        Principal: "*",
        FunctionUrlAuthType: "NONE",
      },
    });
  }
}

Pricing

Lambda function URLs are included in AWS Lambda's pricing - there are no additional charges for using this feature (and you can save money on not using API Gateway)

Limits

Lambda function URLs share the same limits as AWS Lambda functions:

  • 6 MB response and request payload size
  • Default 1,000 up to tens of thousands TPS
  • 15-minute timeout

In short - there is no additional cost, no additional timeout and payload limits when choosing to use Lambda function URLs.

Check out the official AWS docs to learn more.

So, what's the difference between Function URLs and API Gateway? Are Function URLs objectively better?

Not exactly, there's always a tradeoff.

FeatureAPI Gateway REST APIsFunction URLs
HTTPSYesYes
Payload limit6 MB (with AWS Lambda)6 MB
Max timeout29 secondsUp to 15 minutes
SDK generationYesNo
Costfrom $3.50/million for first 333 millionFree
AWS WAFYesNo
Usage plansYesNo
Request/response validation and transformationYesNo

Source - AWS Summit SF 2022 - Best practices for building interactive applications with AWS Lambda (CON302)

Function URLs have a greatly extended timeout (up to 15 minutes) compared to API Gateway + Lambda, with the tradeoff of highly reduced security/usage limits capabilities.

To quote Announcing AWS Lambda Function URLs blogpost:

Function URLs are best for use cases where you must implement a single-function microservice with a public endpoint that doesn’t require the advanced functionality of API Gateway, such as request validation, throttling, custom authorizers, custom domain names, usage plans, or caching. [...] It is also the simplest way to invoke your Lambda functions during research and development without leaving the Lambda console or integrating additional services.

Use API Gateway to take advantage of capabilities like JWT/custom authorizers, request/response validation and transformation, usage plans, built-in AWS WAF support, and so on.

Learn more in Lambda function URLs documentation.

Tired of switching between AWS console tabs? 😒

Cloudash provides a laser-focused view of your AWS API Gateway, Lambda and other resources - all in a single app.

Logs screen