Secure API Gateway to Lambda

Learn to connect API Gateway to Lambda securely with IAM roles, resource policies, and best practices. This lesson covers problem context, core concepts, step-by-step setup, hands-on walkthrough, security options comparison, troubleshooting, and what to learn next in the AWS Tutorial track.

Focus: connect api gateway to lambda securely

Sponsored

Ever created an API Gateway endpoint that works locally but fails in production with a cryptic 403 or 500? Or worse—left your Lambda function wide open for anyone with an AWS account to invoke? Connecting API Gateway to Lambda is trivial when you click through the console, but doing it securely is another story. A misconfigured integration can expose your backend to unauthorized invocations, cost you money, and become a security nightmare. In this lesson, you'll move beyond the happy path and learn how to connect API Gateway to Lambda with identity-based policies, resource policies, and least-privilege best practices—so your API stays fast, reliable, and locked down.

The problem this lesson solves

When you connect API Gateway to Lambda, two distinct security boundaries come into play:

  1. Who can call your API? — This is controlled at the API Gateway level (auth, API keys, throttling).
  2. Who can invoke your Lambda? — This is controlled by Lambda's resource policy and the caller's IAM permissions.

Most tutorials gloss over the second boundary. They create a Lambda function, wire it to API Gateway, and call it done. But here's the catch: AWS managed policies like AWSLambdaBasicExecutionRole do NOT grant API Gateway permission to invoke your function. If you forget to add the right permissions, you'll get a 403 Forbidden from API Gateway when you try to call your endpoint—even though everything looks fine in the console.

Worse, if you use the console's default "create a new role" option without understanding what it does, you might end up with a role that's too permissive. Or you might accidentally leave your Lambda open to invocation from any AWS account or even the public internet (yes, that's possible with a misconfigured resource policy).

The outcome: your API breaks in production, or—if it doesn't break—it becomes a security liability. This lesson gives you a mental model to avoid both, plus a hands-on walkthrough to wire it up correctly from the start.

Core concept / mental model

Think of your Lambda function as a secure vault. It has two doors, each with its own lock:

  • Door 1: The resource policy (attach to the Lambda itself) — this says who (which AWS account or service) can even attempt to enter.
  • Door 2: The IAM identity policy (attach to the caller, e.g., the API Gateway service) — this says what actions that caller can perform on the resource.

For API Gateway to invoke your Lambda, both locks must be open. That means:

  1. The Lambda resource policy must allow lambda:InvokeFunction from the API Gateway service (or from the API's account).
  2. The AWS service (API Gateway) must have permission to call lambda:InvokeFunction on your function—this comes from the Lambda execution role you attach when you create the function or via the API Gateway's service role.

In practice, you attach a Lambda execution role to the Lambda function itself. That role's policy includes a statement like:

{
  "Effect": "Allow",
  "Action": "lambda:InvokeFunction",
  "Resource": "arn:aws:lambda:us-east-1:123456789012:function:my-function"
}

But wait—that's for the execution role (what the Lambda uses to run). For API Gateway to invoke the function, API Gateway needs its own permission, which you grant via a resource policy on the Lambda (also called a function policy) or via a separate API Gateway IAM role. The console often does this automatically, but understanding it helps you debug when things go wrong.

Key terms

  • Lambda resource policy — also called a function policy; it's a JSON policy attached to the function that defines which principals (like apigateway.amazonaws.com) can invoke it.
  • Lambda execution role — the IAM role that your Lambda function assumes at runtime to access other AWS services (like CloudWatch Logs). It's NOT the same as the resource policy.
  • API Gateway IAM role — an optional role that API Gateway assumes to invoke your Lambda when you use a custom integration or need extra permissions.

Visualizing the flow

[Client] -> [API Gateway] -> [Lambda]
                  |               |
                  | (invoke)      | (resource policy allows apigateway)
                  |               |
               [API Gateway] -> [Lambda] -> [CloudWatch Logs]
                  |               |
                  | (execution)   | (execution role grants logs permission)

In words: API Gateway must be allowed to call lambda:InvokeFunction on your function (via resource policy / integration permissions), and the Lambda must have an execution role with permissions for anything it needs to do (like writing logs).

How it works step by step

Here's the logical sequence of what happens when you connect API Gateway to Lambda securely:

  1. Create the Lambda function (or use an existing one).
  2. Define the execution role for the Lambda (for example, arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole). This role is assumed by the Lambda at runtime.
  3. Create an API Gateway REST or HTTP API.
  4. Create a resource and method (e.g., GET /items).
  5. Configure the integration to point to your Lambda function.
  6. Grant API Gateway permission to invoke the Lambda — either automatically via the console (it adds a resource policy) or manually via the AWS CLI/CloudFormation.
  7. Test the endpoint with an API key or IAM auth (if you configured them).

The critical permissions

  • Resource-based policy for API Gateway — Add a statement to your Lambda's resource policy that allows apigateway.amazonaws.com to call lambda:InvokeFunction. If you use the console, this happens automatically when you create the integration. But if you're using Infrastructure as Code (like Terraform or CloudFormation), you must include it explicitly.

  • Lambda execution role for API Gateway's integration — For standard proxy integrations, API Gateway doesn't need a separate IAM role; it uses the Lambda resource policy. However, if you're using custom integrations or need to access other AWS services, you might need a role for API Gateway.

Hands-on walkthrough

Let's do a complete example using the AWS CLI. We'll create a Lambda function, an API Gateway REST API, and connect them securely.

Prerequisites

  • AWS CLI configured with appropriate credentials.
  • Python 3.10+ or 3.11 installed locally (we'll package the Lambda as a zip).

1. Create the Lambda function

First, write a simple function in lambda_function.py:

import json

def lambda_handler(event, context):
    """A simple GET handler."""
    return {
        "statusCode": 200,
        "body": json.dumps({"message": "Hello from secure Lambda!"}),
        "headers": {"Content-Type": "application/json"}
    }

Now package and deploy it:

# Create a deployment package
zip function.zip lambda_function.py

# Create the Lambda function (ensure the execution role exists)
ROLE_ARN=$(aws iam create-role --role-name lambda-api-execution-role --assume-role-policy-document file://trust-policy.json --query 'Role.Arn' --output text)
aws lambda create-function \
  --function-name secure-api-lambda \
  --runtime python3.11 \
  --role $ROLE_ARN \
  --handler lambda_function.lambda_handler \
  --zip-file fileb://function.zip

2. Create an API Gateway REST API

# Create a REST API
API_ID=$(aws apigateway create-rest-api --name 'secure-api' --region us-east-1 --query 'id' --output text)
ROOT_ID=$(aws apigateway get-resources --rest-api-id $API_ID --region us-east-1 --query 'items[0].id' --output text)

# Create a resource /items
RESOURCE_ID=$(aws apigateway create-resource --rest-api-id $API_ID --parent-id $ROOT_ID --path-part items --region us-east-1 --query 'id' --output text)

# Create a GET method
aws apigateway put-method --rest-api-id $API_ID --resource-id $RESOURCE_ID --http-method GET --authorization-type NONE --region us-east-1

# Set up the Lambda proxy integration
aws apigateway put-integration \
  --rest-api-id $API_ID \
  --resource-id $RESOURCE_ID \
  --http-method GET \
  --type AWS_PROXY \
  --integration-http-method POST \
  --uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/$(aws lambda get-function --function-name secure-api-lambda --region us-east-1 --query 'Configuration.FunctionArn' --output text)/invocations \
  --region us-east-1

3. Grant API Gateway permission to invoke the Lambda

This is the step most people forget. Add a resource policy to the Lambda:

aws lambda add-permission \
  --function-name secure-api-lambda \
  --statement-id apigateway-invoke \
  --action lambda:InvokeFunction \
  --principal apigateway.amazonaws.com \
  --source-arn "arn:aws:execute-api:us-east-1:YOUR_ACCOUNT_ID:$API_ID/*/GET/items"

Replace YOUR_ACCOUNT_ID with your AWS account ID. The source-arn restricts permission to only your specific API resource—this is a best practice to prevent other APIs from invoking your Lambda.

4. Deploy the API to a stage

aws apigateway create-deployment --rest-api-id $API_ID --stage-name prod --region us-east-1

# Get the endpoint URL
ENDPOINT="https://$API_ID.execute-api.us-east-1.amazonaws.com/prod/items"
echo "Endpoint: $ENDPOINT"

5. Test it

curl -s $ENDPOINT

Expected output:

{"message": "Hello from secure Lambda!"}

If you got a 403 Forbidden, the most likely cause is a missing or misconfigured resource policy. Let's look at troubleshooting next.

Compare options / when to choose what

You have several ways to secure API Gateway-to-Lambda. Here's a comparison to help you choose:

Option Pros Cons When to use
Resource policy (function policy) Simple, works for most cases, explicit source ARN restriction Requires manual CLI/IaC setup if not using console Default choice for most Lambda + API Gateway integrations
IAM authorization on API Gateway Adds user-level auth, integrates with Cognito/IAM More complex setup, requires clients to sign requests When you need per-user access control for backend services
Lambda execution role with lambda:InvokeFunction Centralizes permissions via IAM, can be reused across functions Not a substitute for resource policy; must be combined with a resource policy for API Gateway When using custom integrations that need extra AWS access
API Gateway resource policy (at API level) Can restrict by IP address or VPC Doesn't control which Lambda the API can invoke For inbound restrictions on the API itself (e.g., allow only specific IPs)

Recommendation

For most use cases, use AWS_PROXY integration with a resource policy that includes source-arn. This gives you the tightest, most readable security model.

Troubleshooting & edge cases

1. 403 Forbidden from API Gateway

  • Cause: Lambda resource policy missing or doesn't match source-arn.
  • Fix: Run aws lambda get-policy --function-name secure-api-lambda to inspect the policy. Add the permission as shown in the walkthrough.

2. 502 Bad Gateway

  • Cause: Lambda function crashed or returned invalid JSON.
  • Fix: Check CloudWatch Logs: aws logs tail /aws/lambda/secure-api-lambda --follow. Also ensure your handler returns a proper response shape (statusCode, body, headers).

3. AccessDeniedException when calling add-permission

  • Cause: Your IAM user doesn't have permissions to modify Lambda policies.
  • Fix: Attach the managed policy AWSLambdaFullAccess or a custom policy with lambda:AddPermission and lambda:GetPolicy.

4. The provided execution role does not have permissions to call ... errors

  • Cause: Your Lambda execution role is missing permissions for services it accesses (e.g., DynamoDB or S3).
  • Fix: Attach the necessary managed policies (e.g., AmazonDynamoDBFullAccess, AmazonS3ReadOnlyAccess) to the Lambda execution role.

Edge case: Cross-account invocations

If you need API Gateway in Account A to invoke a Lambda in Account B, you must: 1. Add a resource policy in Account B allowing apigateway.amazonaws.com from Account A. 2. In Account A, ensure API Gateway has permission (maybe via a role) to invoke that Lambda.

Edge case: Using source-arn with wildcards

Be careful with wildcards like arn:aws:execute-api:us-east-1:123456789012:* — this allows any API in your account to invoke the Lambda, which might be too broad. Restrict to specific API/stage/method as shown.

What you learned & what's next

You now understand the two security layers involved in connecting API Gateway to Lambda: the resource policy (who can invoke) and the execution role (what the Lambda can do). You learned how to add the critical lambda:InvokeFunction permission for API Gateway, restrict it with source-arn, and test your endpoint. You also saw how to troubleshoot common failures like 403 Forbidden and 502 Bad Gateway.

In the next lesson in the AWS Tutorial track, you'll likely explore authenticating API Gateway requests with Cognito or IAM, or securing Lambda functions with VPCs. You'll build on this foundation to add user-level auth to your APIs.

For now, make sure you can recreate this setup in your own account, and remember: never deploy an API Gateway-to-Lambda integration without explicitly granting lambda:InvokeFunction for your API's ARN.

Practice recap

Try recreating the walkthrough in your own AWS account. After you get it working, experiment by removing the resource policy and observe the 403 error, then re-add it. Next, modify the Lambda to access an S3 bucket and ensure the execution role has the correct permissions.

Common mistakes

  • Forgetting to add lambda:InvokeFunction permission for API Gateway — results in 403 Forbidden at runtime.
  • Using a wildcard source-arn like arn:aws:execute-api:* — allows any API in your account to invoke the Lambda, widening the attack surface.
  • Confusing the Lambda execution role with the resource policy — the execution role does NOT grant API Gateway permission to invoke the function.
  • Not restricting the resource policy to a specific API stage or method, which can let other APIs or stages trigger your Lambda.
  • Using the console to create the integration and assuming it is automatically secure — the console may add permissive policies if you choose the wrong options.

Variations

  1. Use AWS SAM or CloudFormation to define the Lambda resource policy declaratively, ensuring reproducibility.
  2. Use API Gateway HTTP API instead of REST API for simpler and cheaper integrations, but note differences in permission model.
  3. Implement IAM authorization on the API Gateway method to require signed requests from clients, adding a second layer of security.

Real-world use cases

  • A serverless REST API for a mobile app, where API Gateway invokes a Lambda that queries DynamoDB, with the Lambda resource policy restricted to the production stage only.
  • A webhook endpoint that accepts JSON payloads, with API Gateway using request validation and a Lambda that processes the event—protected by a source ARN for the specific POST method.
  • An internal microservice exposed within a VPC, using API Gateway private endpoint and Lambda resource policy allowing only the VPC endpoint's source ARN.

Key takeaways

  • API Gateway needs explicit permission to invoke your Lambda — always add a resource policy with lambda:InvokeFunction and restrict it with source-arn.
  • The Lambda execution role is for runtime permissions (like CloudWatch Logs), not for API Gateway's invocation.
  • Use AWS_PROXY integration for simplicity and to avoid payload mapping issues.
  • When troubleshooting 403, first inspect the Lambda resource policy with aws lambda get-policy.
  • Always deploy your API to a stage and test with curl to confirm the integration works end-to-end.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.