Skip to main content

API Gateway REST API Logging Enabled

Overview

This check verifies that your API Gateway REST API stages have CloudWatch logging enabled. Logging captures execution and access information for your APIs, providing visibility into API activity for debugging, monitoring, and security analysis.

Risk

Without logging enabled on your API Gateway stages:

  • Attackers can operate undetected - probing endpoints, exfiltrating data, or tampering with integrations without leaving audit trails
  • Incident response is hindered - you cannot investigate security incidents or troubleshoot issues effectively
  • Compliance gaps - many security frameworks require API activity logging for audit purposes
  • No visibility into API usage patterns - making it difficult to detect abuse or anomalies

Remediation Steps

Prerequisites

  • AWS account access with permission to modify API Gateway settings
  • An IAM role that API Gateway can assume to write to CloudWatch Logs (you may need to create this first)
Creating the CloudWatch Logs role

Before enabling logging, you need an IAM role that allows API Gateway to write to CloudWatch Logs. This is a one-time setup per account.

Console method:

  1. Go to IAM > Roles > Create role
  2. Select API Gateway as the trusted entity
  3. Attach the managed policy AmazonAPIGatewayPushToCloudWatchLogs
  4. Name the role (e.g., APIGatewayCloudWatchLogsRole)
  5. Note the role ARN for the next steps

CLI method:

# Create the trust policy
cat > trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF

# Create the role
aws iam create-role \
--role-name APIGatewayCloudWatchLogsRole \
--assume-role-policy-document file://trust-policy.json \
--region us-east-1

# Attach the managed policy
aws iam attach-role-policy \
--role-name APIGatewayCloudWatchLogsRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs

AWS Console Method

  1. Sign in to the AWS Management Console
  2. Navigate to API Gateway
  3. In the left navigation, click Settings (at the bottom)
  4. Under CloudWatch log role ARN, enter the ARN of your logging role and click Save
  5. Go back to APIs and select your REST API
  6. In the left navigation, click Stages
  7. Select the stage you want to configure (e.g., prod, dev)
  8. Click the Logs and tracing tab
  9. Under CloudWatch Logs, set:
    • Log level: Select Errors only or Errors and info
    • Data tracing: Leave disabled unless you need full request/response bodies (may log sensitive data)
  10. Click Save Changes

Repeat steps 7-10 for each stage that needs logging enabled.

AWS CLI (optional)

First, configure the account-level CloudWatch role (one-time setup):

aws apigateway update-account \
--patch-operations op=replace,path=/cloudwatchRoleArn,value=arn:aws:iam::<ACCOUNT_ID>:role/APIGatewayCloudWatchLogsRole \
--region us-east-1

Then enable logging for a specific stage:

aws apigateway update-stage \
--rest-api-id <REST_API_ID> \
--stage-name <STAGE_NAME> \
--patch-operations 'op=replace,path=/*/*/logging/loglevel,value=ERROR' \
--region us-east-1

Log level options:

  • OFF - No logging
  • ERROR - Log errors only (recommended minimum)
  • INFO - Log all requests and responses

To also enable metrics:

aws apigateway update-stage \
--rest-api-id <REST_API_ID> \
--stage-name <STAGE_NAME> \
--patch-operations 'op=replace,path=/*/*/logging/loglevel,value=ERROR' 'op=replace,path=/*/*/metrics/enabled,value=true' \
--region us-east-1

To list your REST APIs and find their IDs:

aws apigateway get-rest-apis --region us-east-1

To list stages for a specific API:

aws apigateway get-stages --rest-api-id <REST_API_ID> --region us-east-1
CloudFormation (optional)

This template configures logging for an API Gateway stage:

AWSTemplateFormatVersion: '2010-09-09'
Description: Enable CloudWatch logging for API Gateway REST API stages

Parameters:
RestApiId:
Type: String
Description: The ID of the REST API
StageName:
Type: String
Description: The name of the stage
CloudWatchRoleArn:
Type: String
Description: ARN of the IAM role that API Gateway can assume to write logs

Resources:
ApiGatewayAccount:
Type: AWS::ApiGateway::Account
Properties:
CloudWatchRoleArn: !Ref CloudWatchRoleArn

ApiGatewayStage:
Type: AWS::ApiGateway::Stage
DependsOn: ApiGatewayAccount
Properties:
RestApiId: !Ref RestApiId
StageName: !Ref StageName
MethodSettings:
- HttpMethod: '*'
ResourcePath: '/*'
LoggingLevel: ERROR
DataTraceEnabled: false
MetricsEnabled: true

Outputs:
StageArn:
Description: ARN of the configured stage
Value: !Sub 'arn:aws:apigateway:${AWS::Region}::/restapis/${RestApiId}/stages/${StageName}'

Important: If you already have a stage defined in CloudFormation, simply add the MethodSettings block to your existing stage resource rather than creating a new one.

Deploy the template:

aws cloudformation deploy \
--template-file apigateway-logging.yaml \
--stack-name apigateway-logging-stack \
--parameter-overrides \
RestApiId=<REST_API_ID> \
StageName=<STAGE_NAME> \
CloudWatchRoleArn=arn:aws:iam::<ACCOUNT_ID>:role/APIGatewayCloudWatchLogsRole \
--region us-east-1
Terraform (optional)

First, create the IAM role for CloudWatch logging:

resource "aws_iam_role" "api_gateway_cloudwatch" {
name = "APIGatewayCloudWatchLogsRole"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "apigateway.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}

resource "aws_iam_role_policy_attachment" "api_gateway_cloudwatch" {
role = aws_iam_role.api_gateway_cloudwatch.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
}

resource "aws_api_gateway_account" "main" {
cloudwatch_role_arn = aws_iam_role.api_gateway_cloudwatch.arn
}

Then configure logging on your stage:

resource "aws_api_gateway_stage" "example" {
deployment_id = aws_api_gateway_deployment.example.id
rest_api_id = aws_api_gateway_rest_api.example.id
stage_name = "prod"

access_log_settings {
destination_arn = aws_cloudwatch_log_group.api_gateway.arn
format = jsonencode({
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"
integrationStatus = "$context.integrationStatus"
})
}

depends_on = [aws_api_gateway_account.main]
}

resource "aws_api_gateway_method_settings" "all" {
rest_api_id = aws_api_gateway_rest_api.example.id
stage_name = aws_api_gateway_stage.example.stage_name
method_path = "*/*"

settings {
logging_level = "ERROR"
data_trace_enabled = false
metrics_enabled = true
}
}

resource "aws_cloudwatch_log_group" "api_gateway" {
name = "/aws/api-gateway/${aws_api_gateway_rest_api.example.name}"
retention_in_days = 30
}

Verification

After enabling logging, verify the configuration:

  1. Go to API Gateway > APIs > select your API > Stages
  2. Select your stage and click the Logs and tracing tab
  3. Confirm CloudWatch Logs shows a log level of ERROR or INFO
  4. Make a test API call, then check CloudWatch Logs for log entries
CLI verification commands

Check the stage configuration:

aws apigateway get-stage \
--rest-api-id <REST_API_ID> \
--stage-name <STAGE_NAME> \
--region us-east-1

Look for methodSettings in the output with logging enabled:

{
"methodSettings": {
"*/*": {
"loggingLevel": "ERROR",
"dataTraceEnabled": false,
"metricsEnabled": true
}
}
}

Verify the account has a CloudWatch role configured:

aws apigateway get-account --region us-east-1

Check CloudWatch for log groups (after making API calls):

aws logs describe-log-groups \
--log-group-name-prefix "API-Gateway-Execution-Logs" \
--region us-east-1

Additional Resources

Notes

  • Account-level role required: The CloudWatch Logs role is configured at the account level, not per-API. You only need to set this up once per region.
  • Log levels: Use ERROR for production to capture problems without excessive logging. Use INFO during debugging or when you need full visibility.
  • Data tracing caution: Enabling DataTraceEnabled logs full request and response bodies. This can expose sensitive data in your logs - use carefully.
  • Cost considerations: CloudWatch Logs charges for ingestion and storage. Consider setting retention policies on log groups to manage costs.
  • Multi-region: If you have APIs in multiple regions, repeat this configuration in each region.
  • Log group naming: API Gateway creates log groups named API-Gateway-Execution-Logs_<rest-api-id>/<stage-name>.