Networking & Content DeliveryIntermediate15 min read

    Amazon API Gateway Security Best Practices

    Tarek Cheikh

    Founder & AWS Security Expert

    View Security Card

    Amazon API Gateway is the front door to your cloud applications, handling authentication, request routing, throttling, and protocol transformation for REST, HTTP, and WebSocket APIs. As the first component that processes inbound traffic, API Gateway is also the first target for attackers -- injection attempts, credential stuffing, enumeration attacks, and volumetric abuse all arrive here before reaching your backend services.

    In 2025, the OWASP API Security Top 10 highlighted Broken Authentication, Broken Object Level Authorization, and Unrestricted Resource Consumption as the most exploited API vulnerabilities. AWS Security Hub data shows that a significant number of API Gateway deployments still lack WAF protection, have execution logging disabled, or expose routes without any authorization type configured. Each of these gaps represents a direct path for attackers to exfiltrate data, abuse compute resources, or pivot deeper into your environment.

    This guide covers 12 essential Amazon API Gateway security best practices, each with real AWS CLI commands, verification procedures, and references to Security Hub control IDs. Whether you are running REST APIs, HTTP APIs, or WebSocket APIs, these controls form a comprehensive defense-in-depth strategy for your API layer.

    1. Enable Execution and Access Logging

    Without logging, you are operating blind. Execution logging captures detailed records of every request processed by API Gateway, including integration backend responses, Lambda authorizer responses, and AWS integration request IDs. Access logging gives you structured, customizable logs for analytics, anomaly detection, and compliance audits. Security Hub flags this as [APIGateway.1] for REST/WebSocket APIs and [APIGateway.9] for HTTP APIs.

    Implementation

    First, ensure the API Gateway service has permission to write to CloudWatch by creating a service-linked role. Then enable both execution and access logging on each stage.

    # Create a CloudWatch log group for access logs
    aws logs create-log-group \
      --log-group-name "/aws/apigateway/my-api-access-logs"
    
    # Enable execution logging (ERROR or INFO level) on a REST API stage
    aws apigateway update-stage \
      --rest-api-id abc123def4 \
      --stage-name prod \
      --patch-operations \
        op=replace,path=/*/*/logging/loglevel,value=INFO \
        op=replace,path=/*/*/logging/dataTrace,value=false
    
    # Enable access logging on a REST API stage
    aws apigateway update-stage \
      --rest-api-id abc123def4 \
      --stage-name prod \
      --patch-operations \
        op=replace,path=/accessLogSettings/destinationArn,value=arn:aws:logs:us-east-1:123456789012:log-group:/aws/apigateway/my-api-access-logs \
        op=replace,path=/accessLogSettings/format,value='{"requestId":"$context.requestId","ip":"$context.identity.sourceIp","caller":"$context.identity.caller","user":"$context.identity.user","requestTime":"$context.requestTime","httpMethod":"$context.httpMethod","resourcePath":"$context.resourcePath","status":"$context.status","protocol":"$context.protocol","responseLength":"$context.responseLength"}'

    Verification

    # Verify logging configuration on a stage
    aws apigateway get-stage \
      --rest-api-id abc123def4 \
      --stage-name prod \
      --query '{ExecutionLog: methodSettings."*/*".loggingLevel, AccessLog: accessLogSettings}'

    Security Hub Controls: [APIGateway.1] REST and WebSocket API execution logging should be enabled. [APIGateway.9] Access logging should be configured for API Gateway V2 Stages.

    2. Enforce Authentication on Every Route

    Every API route that lacks an authorization type is publicly accessible to the entire internet. This is one of the most common API Gateway misconfigurations. Security Hub control [APIGateway.8] specifically checks that API Gateway routes specify an authorization type. You have four options: IAM authorization, Cognito user pools, Lambda authorizers, or JWT authorizers (HTTP APIs).

    Implementation

    # Set IAM authorization on a REST API method
    aws apigateway update-method \
      --rest-api-id abc123def4 \
      --resource-id xyz789 \
      --http-method GET \
      --patch-operations op=replace,path=/authorizationType,value=AWS_IAM
    
    # Create a Cognito authorizer for a REST API
    aws apigateway create-authorizer \
      --rest-api-id abc123def4 \
      --name "CognitoAuth" \
      --type COGNITO_USER_POOLS \
      --provider-arns arn:aws:cognito-idp:us-east-1:123456789012:userpool/us-east-1_ExamplePool \
      --identity-source "method.request.header.Authorization"
    
    # Attach the Cognito authorizer to a method
    aws apigateway update-method \
      --rest-api-id abc123def4 \
      --resource-id xyz789 \
      --http-method GET \
      --patch-operations \
        op=replace,path=/authorizationType,value=COGNITO_USER_POOLS \
        op=replace,path=/authorizerId,value=auth123

    Audit

    # List all methods and their authorization types for a REST API
    aws apigateway get-resources \
      --rest-api-id abc123def4 \
      --query "items[].{Path: path, Methods: resourceMethods}"

    Security Hub Control: [APIGateway.8] API Gateway routes should specify an authorization type.

    3. Use Lambda Authorizers for Custom Authorization Logic

    Lambda authorizers (formerly custom authorizers) give you full control over authentication and authorization logic. Use a REQUEST-type Lambda authorizer to inspect headers, query strings, stage variables, and context variables. This approach supports third-party identity providers, custom token formats, multi-tenant authorization, and fine-grained access control based on Cognito groups or claims.

    Implementation

    # Create a REQUEST-type Lambda authorizer
    aws apigateway create-authorizer \
      --rest-api-id abc123def4 \
      --name "CustomLambdaAuth" \
      --type REQUEST \
      --authorizer-uri "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:my-authorizer/invocations" \
      --authorizer-result-ttl-in-seconds 300 \
      --identity-source "method.request.header.Authorization,context.accountId"
    
    # Grant API Gateway permission to invoke the Lambda authorizer
    aws lambda add-permission \
      --function-name my-authorizer \
      --statement-id apigateway-invoke \
      --action lambda:InvokeFunction \
      --principal apigateway.amazonaws.com \
      --source-arn "arn:aws:execute-api:us-east-1:123456789012:abc123def4/authorizers/auth456"

    Best Practices: Always use REQUEST-type authorizers instead of TOKEN-type, as they provide access to the full request context. Enable authorizer result caching (TTL of 300 seconds is a good default) to reduce Lambda invocations and latency. Return explicit Deny policies for unauthorized requests rather than raising exceptions.

    4. Associate AWS WAF with Every API Stage

    AWS WAF inspects HTTP requests before they reach your API logic, blocking SQL injection, XSS, bot traffic, and volumetric abuse at the edge. Without WAF, malicious payloads pass directly to your Lambda functions or backend integrations. Note that WAF integration is available for REST APIs only -- HTTP APIs do not support WAF directly and must be fronted by CloudFront with WAF for equivalent protection.

    Implementation

    # Create a WAF web ACL for API Gateway (regional scope)
    aws wafv2 create-web-acl \
      --name "api-gateway-waf" \
      --scope REGIONAL \
      --default-action Allow={} \
      --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=ApiGatewayWAF \
      --rules '[
        {
          "Name": "AWSManagedRulesCommonRuleSet",
          "Priority": 1,
          "Statement": {
            "ManagedRuleGroupStatement": {
              "VendorName": "AWS",
              "Name": "AWSManagedRulesCommonRuleSet"
            }
          },
          "OverrideAction": {"None": {}},
          "VisibilityConfig": {
            "SampledRequestsEnabled": true,
            "CloudWatchMetricsEnabled": true,
            "MetricName": "CommonRuleSet"
          }
        }
      ]' \
      --region us-east-1
    
    # Associate WAF web ACL with the API Gateway stage
    aws wafv2 associate-web-acl \
      --web-acl-arn arn:aws:wafv2:us-east-1:123456789012:regional/webacl/api-gateway-waf/example-id \
      --resource-arn arn:aws:apigateway:us-east-1::/restapis/abc123def4/stages/prod

    Verification

    # Check which WAF web ACL is associated with an API stage
    aws wafv2 get-web-acl-for-resource \
      --resource-arn arn:aws:apigateway:us-east-1::/restapis/abc123def4/stages/prod

    Security Hub Control: [APIGateway.4] API Gateway should be associated with a WAF Web ACL. For recommended WAF rule groups, see our WAF security card.

    5. Enable Mutual TLS (mTLS) Authentication

    Mutual TLS adds certificate-based client authentication on top of standard TLS encryption. Instead of relying solely on API keys or tokens, the client must present a valid X.509 certificate signed by a trusted Certificate Authority. This is essential for B2B integrations, service-to-service communication, and any scenario requiring strong non-repudiation. mTLS is available for both REST APIs and HTTP APIs at no additional cost.

    Implementation

    # Upload your CA certificate bundle (truststore) to S3
    aws s3 cp truststore.pem s3://my-bucket/truststore.pem
    
    # Create or update a custom domain name with mutual TLS enabled
    aws apigatewayv2 create-domain-name \
      --domain-name api.example.com \
      --domain-name-configurations CertificateArn=arn:aws:acm:us-east-1:123456789012:certificate/example-cert-id,EndpointType=REGIONAL,SecurityPolicy=TLS_1_2 \
      --mutual-tls-authentication TruststoreUri=s3://my-bucket/truststore.pem
    
    # For REST APIs, use the v1 API
    aws apigateway create-domain-name \
      --domain-name api.example.com \
      --regional-certificate-arn arn:aws:acm:us-east-1:123456789012:certificate/example-cert-id \
      --security-policy TLS_1_2 \
      --mutual-tls-authentication truststoreUri=s3://my-bucket/truststore.pem \
      --endpoint-configuration types=REGIONAL

    Important: mTLS requires a Regional custom domain name -- it cannot be used with the default API Gateway endpoint. Use AWS Certificate Manager Private CA (ACM PCA) for issuing client certificates in production environments.

    6. Apply Resource Policies to Restrict API Access

    Resource policies are JSON-based access policies attached to an API that control whether a specified principal (AWS account, IP range, or VPC endpoint) can invoke the API. They act as a perimeter control, evaluated before your authorizer logic runs. Use resource policies to restrict access by source IP, AWS account, or VPC endpoint.

    Implementation

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Deny",
          "Principal": "*",
          "Action": "execute-api:Invoke",
          "Resource": "arn:aws:execute-api:us-east-1:123456789012:abc123def4/*",
          "Condition": {
            "NotIpAddress": {
              "aws:SourceIp": ["203.0.113.0/24", "198.51.100.0/24"]
            }
          }
        },
        {
          "Effect": "Allow",
          "Principal": "*",
          "Action": "execute-api:Invoke",
          "Resource": "arn:aws:execute-api:us-east-1:123456789012:abc123def4/*"
        }
      ]
    }
    # Apply a resource policy to a REST API
    aws apigateway update-rest-api \
      --rest-api-id abc123def4 \
      --patch-operations op=replace,path=/policy,value='{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":"*","Action":"execute-api:Invoke","Resource":"arn:aws:execute-api:us-east-1:123456789012:abc123def4/*","Condition":{"StringEquals":{"aws:SourceVpce":"vpce-0123456789abcdef0"}}}]}'
    
    # After updating the resource policy, redeploy the API
    aws apigateway create-deployment \
      --rest-api-id abc123def4 \
      --stage-name prod

    Evaluation order: WAF rules are evaluated first, then resource policies, then authentication/authorization. A deny at any layer blocks the request.

    7. Create Private APIs with VPC Endpoints

    Private APIs are only accessible from within your VPC through interface VPC endpoints (AWS PrivateLink). They never traverse the public internet, eliminating an entire class of network-based attacks. Use private APIs for internal microservice communication, backend-for-frontend patterns, and any API that should not be publicly reachable.

    Implementation

    # Create a VPC endpoint for API Gateway
    aws ec2 create-vpc-endpoint \
      --vpc-id vpc-0123456789abcdef0 \
      --service-name com.amazonaws.us-east-1.execute-api \
      --vpc-endpoint-type Interface \
      --subnet-ids subnet-0123456789abcdef0 subnet-0123456789abcdef1 \
      --security-group-ids sg-0123456789abcdef0 \
      --private-dns-enabled
    
    # Create a private REST API
    aws apigateway create-rest-api \
      --name "private-api" \
      --endpoint-configuration types=PRIVATE,vpcEndpointIds=vpce-0123456789abcdef0
    
    # Attach a resource policy restricting access to the VPC endpoint
    aws apigateway update-rest-api \
      --rest-api-id priv123 \
      --patch-operations op=replace,path=/policy,value='{"Version":"2012-10-17","Statement":[{"Effect":"Deny","Principal":"*","Action":"execute-api:Invoke","Resource":"arn:aws:execute-api:us-east-1:123456789012:priv123/*","Condition":{"StringNotEquals":{"aws:SourceVpce":"vpce-0123456789abcdef0"}}},{"Effect":"Allow","Principal":"*","Action":"execute-api:Invoke","Resource":"arn:aws:execute-api:us-east-1:123456789012:priv123/*"}]}'

    Tip: Restrict the VPC endpoint security group to allow inbound HTTPS (port 443) only from the CIDR ranges of your application subnets.

    8. Configure Throttling and Usage Plans

    API Gateway provides account-level throttling (10,000 RPS default with 5,000 burst) and stage/method-level throttling. Without explicit throttling, a single abusive client can consume your entire account quota, causing denial of service for all your APIs. Usage plans with API keys allow you to set per-client rate limits and quotas.

    Implementation

    # Set stage-level throttling
    aws apigateway update-stage \
      --rest-api-id abc123def4 \
      --stage-name prod \
      --patch-operations \
        op=replace,path=/*/*/throttling/rateLimit,value=1000 \
        op=replace,path=/*/*/throttling/burstLimit,value=500
    
    # Create a usage plan with throttling and quota
    aws apigateway create-usage-plan \
      --name "Standard" \
      --throttle burstLimit=200,rateLimit=100 \
      --quota limit=10000,period=DAY \
      --api-stages apiId=abc123def4,stage=prod
    
    # Create an API key and associate it with the usage plan
    aws apigateway create-api-key \
      --name "partner-key" \
      --enabled
    
    aws apigateway create-usage-plan-key \
      --usage-plan-id plan123 \
      --key-id key456 \
      --key-type API_KEY

    Important: API keys are not a security mechanism for authentication -- they are identifiers for usage tracking and throttling. Always combine API keys with a proper authorizer (IAM, Cognito, or Lambda).

    9. Enforce TLS 1.2 Minimum and Configure SSL Certificates for Backend Auth

    API Gateway supports TLS security policies that determine the minimum TLS version and cipher suites used for HTTPS connections. Enforce TLS 1.2 as the minimum to prevent downgrade attacks. For backend authentication, configure API Gateway to present a client certificate when calling your integration endpoints, allowing your backends to verify that requests originate from API Gateway.

    Implementation

    # Set TLS 1.2 security policy on a custom domain name
    aws apigateway update-domain-name \
      --domain-name api.example.com \
      --patch-operations op=replace,path=/securityPolicy,value=TLS_1_2
    
    # Generate a client certificate for backend authentication
    aws apigateway generate-client-certificate \
      --description "Backend auth certificate for prod stage"
    
    # Associate the client certificate with a stage
    aws apigateway update-stage \
      --rest-api-id abc123def4 \
      --stage-name prod \
      --patch-operations op=replace,path=/clientCertificateId,value=cert789

    Verification

    # Check the security policy on a custom domain
    aws apigateway get-domain-name \
      --domain-name api.example.com \
      --query "securityPolicy"
    
    # Verify client certificate is configured on a stage
    aws apigateway get-stage \
      --rest-api-id abc123def4 \
      --stage-name prod \
      --query "clientCertificateId"

    Security Hub Controls: [APIGateway.2] API Gateway REST API stages should be configured to use SSL certificates for backend authentication.

    10. Enable Request Validation

    Request validation allows API Gateway to reject malformed requests before they reach your backend, reducing the attack surface for injection and fuzzing attacks. You can validate request bodies against JSON Schema models, required headers, and required query string parameters. This shifts input validation to the API layer, providing defense in depth.

    Implementation

    # Create a request validator
    aws apigateway create-request-validator \
      --rest-api-id abc123def4 \
      --name "validate-body-and-params" \
      --validate-request-body \
      --validate-request-parameters
    
    # Create a model for request body validation
    aws apigateway create-model \
      --rest-api-id abc123def4 \
      --name "CreateUserModel" \
      --content-type "application/json" \
      --schema '{
        "type": "object",
        "required": ["email", "name"],
        "properties": {
          "email": {"type": "string", "format": "email"},
          "name": {"type": "string", "minLength": 1, "maxLength": 100}
        },
        "additionalProperties": false
      }'
    
    # Apply the validator and model to a method
    aws apigateway update-method \
      --rest-api-id abc123def4 \
      --resource-id xyz789 \
      --http-method POST \
      --patch-operations \
        op=replace,path=/requestValidatorId,value=validator123 \
        op=replace,path=/requestModels/application~1json,value=CreateUserModel

    Tip: Set additionalProperties: false in your JSON Schema models to prevent unexpected fields from being passed through to your backend.

    11. Enable AWS X-Ray Tracing

    X-Ray tracing provides end-to-end visibility into API requests as they flow from API Gateway through Lambda functions, DynamoDB tables, and other downstream services. While primarily a performance tool, X-Ray is invaluable for security incident investigation -- you can trace a suspicious request from ingestion through every backend service it touched.

    Implementation

    # Enable X-Ray tracing on a REST API stage
    aws apigateway update-stage \
      --rest-api-id abc123def4 \
      --stage-name prod \
      --patch-operations op=replace,path=/tracingEnabled,value=true
    
    # Verify X-Ray tracing is enabled
    aws apigateway get-stage \
      --rest-api-id abc123def4 \
      --stage-name prod \
      --query "tracingEnabled"

    Security Hub Control: [APIGateway.3] API Gateway REST API stages should have AWS X-Ray tracing enabled.

    12. Encrypt Cache Data at Rest

    If you enable API response caching on your REST API stages, the cached data is stored in a dedicated cache cluster. By default, this cached data is not encrypted at rest. Enabling cache encryption ensures that sensitive response data is protected even if the underlying cache infrastructure is compromised. This is especially critical for APIs that return PII, financial data, or authentication tokens.

    Implementation

    # Enable caching with encryption on a stage
    aws apigateway update-stage \
      --rest-api-id abc123def4 \
      --stage-name prod \
      --patch-operations \
        op=replace,path=/*/*/caching/enabled,value=true \
        op=replace,path=/*/*/caching/dataEncrypted,value=true \
        op=replace,path=/*/*/caching/ttlInSeconds,value=300
    
    # Verify cache encryption is enabled
    aws apigateway get-stage \
      --rest-api-id abc123def4 \
      --stage-name prod \
      --query "methodSettings."*/*".{CachingEnabled: cachingEnabled, CacheEncrypted: cacheDataEncrypted}"

    Security Hub Control: [APIGateway.5] API Gateway REST API cache data should be encrypted at rest.

    Common Misconfigurations

    These are the most frequently observed API Gateway security issues across production environments:

    • No authorization on routes: Deploying API routes with authorizationType: NONE exposes endpoints to the public internet. Every route must have an authorizer configured.
    • Using API keys as the sole authentication mechanism: API keys are designed for throttling and usage tracking, not security. They are easily leaked in client-side code, browser history, and server logs. Always pair API keys with a proper authorizer.
    • Default execute-api endpoint left enabled: When using a custom domain with mTLS, the default https://{api-id}.execute-api.{region}.amazonaws.com endpoint bypasses mTLS. Disable it using aws apigateway update-rest-api --patch-operations op=replace,path=/disableExecuteApiEndpoint,value=true.
    • Overly permissive resource policies: Resource policies with "Principal": "*" and no conditions allow access from any AWS account or IP address. Always scope resource policies with IP, VPC endpoint, or account conditions.
    • Execution logging disabled: Without execution logging, you cannot diagnose authorization failures, integration errors, or malicious request patterns. Enable at least ERROR-level logging on all stages.
    • No WAF on REST APIs: REST APIs without WAF are directly exposed to OWASP Top 10 attacks. If using HTTP APIs, front them with CloudFront and attach WAF there.
    • Unbounded throttling: Not configuring stage-level or method-level throttling means a single client can consume your entire account-level quota (10,000 RPS), affecting all APIs in the account.
    • Cache without encryption: Enabling API response caching without encryption stores sensitive response data in plaintext on the cache cluster.
    • Wildcard CORS configuration: Setting Access-Control-Allow-Origin: * allows any website to make authenticated requests to your API. Always restrict CORS to your specific domains.

    Quick Reference Checklist

    Best Practice Security Hub Control Severity API Type
    Enable execution logging [APIGateway.1] Medium REST, WebSocket
    Enable access logging (V2) [APIGateway.9] Medium HTTP
    Enforce authorization on all routes [APIGateway.8] Medium HTTP
    Configure SSL certificates for backend auth [APIGateway.2] Medium REST
    Enable X-Ray tracing [APIGateway.3] Low REST
    Associate WAF web ACL [APIGateway.4] Medium REST
    Encrypt cache data at rest [APIGateway.5] Medium REST
    Use HTTPS for private integrations [APIGateway.10] Medium HTTP
    Enable mutual TLS -- High (recommended) REST, HTTP
    Apply resource policies -- High (recommended) REST
    Configure throttling and usage plans -- Medium (recommended) REST
    Enable request validation -- Medium (recommended) REST

    For a concise overview of API Gateway security controls, see our API Gateway security card. For WAF configuration details referenced in this guide, review our AWS WAF Security Best Practices guide. To learn more about securing Lambda authorizer functions, see the AWS Lambda Security Best Practices guide.

    Go Deeper: The State of AWS Security 2026

    This article is just the start. Get the full picture with our free whitepaper - 8 chapters covering IAM, S3, VPC, monitoring, agentic AI security, compliance, and a prioritized action plan with 50+ CLI commands.

    API GatewayREST APIHTTP APIWebSocketAuthenticationAuthorizationWAFMutual TLSLambda AuthorizerCognitoThrottlingVPC EndpointResource Policy