ALB Access Logs: Enable, Read, and Query with Athena
Last verified on Nov 8, 2026.
Application Load Balancer access logs record every request the load balancer receives, including requests that never reach a target. They are off by default. Once enabled, each load balancer node writes a compressed log file to S3 every 5 minutes, which you can query with Athena or read with zcat and awk.
What ALB Access Logs Capture
Each entry is one request, or for WebSockets one connection, written after the connection closes. An entry includes the request time, client address and port, the target that handled it, three latency timings, the status the load balancer returned and the status the target returned, byte counts, the request line, and the user agent. Requests that fail before reaching a target are still logged, for example a malformed request or one with no healthy targets.
AWS describes delivery as best effort: use the logs to understand the nature of your traffic, not as a complete accounting of every request. Delivery is eventually consistent, and the load balancer can deliver more than one file for the same period on a busy site.
Enabling ALB Access Logs
You need a bucket in the same Region as the load balancer, using default encryption with Amazon S3-managed keys (SSE-S3). SSE-KMS is not supported. The bucket and load balancer can belong to different accounts.
Attach a bucket policy that allows the log delivery service to write under the AWSLogs/<account-id>/ path. Always include your account ID in the resource path, and do not use a wildcard for it:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "logdelivery.elasticloadbalancing.amazonaws.com" },
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket/logging-prefix/AWSLogs/123456789012/*"
}
]
}If you set no prefix, drop it from the ARN. If you set one, it must not contain the string AWSLogs. Then enable logging in the console under Load Balancers, your load balancer, Attributes, Edit, Monitoring, or with the CLI:
aws elbv2 modify-load-balancer-attributes \
--load-balancer-arn <load-balancer-arn> \
--attributes \
Key=access_logs.s3.enabled,Value=true \
Key=access_logs.s3.bucket,Value=amzn-s3-demo-bucket \
Key=access_logs.s3.prefix,Value=logging-prefixWhen you enable logging, Elastic Load Balancing writes a test object called ELBAccessLogTestFile under AWSLogs/<account-id>/. If it is missing, check the bucket policy against the Region, the bucket name, and the prefix, then check the encryption setting. Disable access logs before deleting the bucket, otherwise a new bucket with the same name in an account you do not own could receive your logs.
Where the Files Land
Files are named bucket/prefix/AWSLogs/<account-id>/elasticloadbalancing/<region>/<yyyy>/<mm>/<dd>/<account-id>_elasticloadbalancing_<region>_app.<lb-id>_<end-time>_<node-ip>_<random>.log.gz. The end-time is the end of the 5-minute interval, so 20220215T2340Z covers requests from 23
Reading an Entry
Fields are space-delimited, and quoted fields contain spaces. This is an HTTPS entry from the AWS docs, wrapped for reading:
https 2018-07-02T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188
192.168.131.39:2817 10.0.0.1:80 0.086 0.048 0.037 200 200 0 57
"GET https://www.example.com:443/ HTTP/1.1" "curl/7.46.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2
arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067
"Root=1-58337281-1d84f3d73c47ec4e58577259" "www.example.com" "arn:aws:acm:..."
1 2018-07-02T22:22:48.364000Z "authenticate,forward" "-" "-" "10.0.0.1:80" "200" "-" "-"
TID_1234abcd5678ef90 "m.example.com" "-" "TransformSuccess"The fields you will use most, by position:
Comparing fields 9 and 10 separates a failure at the load balancer from one at your service. Field 10 is recorded only when a connection was made to the target and the target sent a response, so a - there means the target never answered and the status in field 9 came from the load balancer. A status in both fields means the target responded. A -1 in request_processing_time means the load balancer could not dispatch the request to a target, for example after a malformed request.
AWS adds new fields to the end of the entry, and may show a trailing - before a field is released. Configure parsers to stop after the last documented field and update them when AWS announces one.
Querying ALB Logs from the Command Line
For a quick look, sync one day and filter on position:
aws s3 cp s3://amzn-s3-demo-bucket/logging-prefix/AWSLogs/123456789012/elasticloadbalancing/us-east-2/2026/11/08/ ./alb --recursive
zcat ./alb/*.log.gz | awk '$9 >= 500 { print $2, $4, $9, $10, $7 }'The $9 test works because the first twelve fields never contain spaces. The request line is the first field that can.
Querying ALB Logs with Athena
Athena queries the compressed files in place. Create a database in the Region of the bucket, then run the CREATE EXTERNAL TABLE statement from AWS's Athena guide for ALB access logs, replacing LOCATION with s3://<bucket>/<prefix>/AWSLogs/<account-id>/elasticloadbalancing/<region>/. The table is called alb_access_logs and uses a regex SerDe. Keep the optional group at the end of the regex, which absorbs fields AWS adds later. AWS also documents a partition projection variant that limits scans to the days you name.
-- Server errors, newest first
SELECT time, client_ip, elb_status_code, target_status_code, request_url
FROM alb_access_logs
WHERE elb_status_code >= 500
ORDER BY time DESC
LIMIT 100;
-- Slowest targets
SELECT target_ip, count(*) AS requests,
avg(target_processing_time) AS avg_seconds,
max(target_processing_time) AS max_seconds
FROM alb_access_logs
WHERE target_processing_time >= 0
GROUP BY target_ip
ORDER BY avg_seconds DESC;
-- Busiest clients
SELECT client_ip, count(*) AS requests, sum(received_bytes) AS bytes_in
FROM alb_access_logs
GROUP BY client_ip
ORDER BY requests DESC
LIMIT 20;Add a time range using parse_datetime(time, 'yyyy-MM-dd''T''HH:mm:ss.SSSSSS''Z') as AWS does, or the day partition column if you used projection, so Athena scans less data.
Delivering ALB Logs to CloudWatch Logs
ALB can also deliver access, connection, and health check logs to CloudWatch Logs, Amazon Data Firehose, or S3, with Parquet output supported for S3. Configure it from the load balancer's Integrations tab. This route lets you use CloudWatch Logs Insights, metric filters, and Live Tail. The S3 route above is called legacy logging by AWS, and it remains available. For Insights syntax, see the Logs Insights guide.
Searching Load Balancer Logs with Sazabi
Sazabi, an AI-native observability and security platform, reads AWS logs from CloudWatch log groups. If you deliver ALB logs to CloudWatch Logs, connect the account under Settings > Log streams > Add source > CloudWatch, then choose the ALB log group with Add log stream. Sazabi installs a subscription filter on the group, and the group must be in the same region as your Sazabi project. Setup details are in the CloudWatch source docs and on the CloudWatch integration page. If a web application firewall sits in front of the load balancer, the AWS WAF integration covers those logs.
Frequently Asked Questions
How do I enable ALB access logs?
Create an S3 bucket in the same Region with SSE-S3 encryption, attach a bucket policy that lets logdelivery.elasticloadbalancing.amazonaws.com call s3:PutObject, then turn on Access logs under the load balancer Attributes tab or set the access_logs.s3.enabled attribute with aws elbv2 modify-load-balancer-attributes.
Where are ALB access logs stored?
In the S3 bucket you specify, as gzip files under AWSLogs/account-id/elasticloadbalancing/region/yyyy/mm/dd/. Each load balancer node publishes a file every 5 minutes.
What is the difference between elb_status_code and target_status_code?
elb_status_code is the status the load balancer returned to the client. target_status_code is the status the target returned to the load balancer, and it is a dash if no connection was made or the target sent no response.
Why are no ALB access logs appearing in my bucket?
Look for the ELBAccessLogTestFile object that ALB writes when you enable logging. If it is missing, the bucket policy is wrong for the Region or prefix, or the bucket uses SSE-KMS instead of SSE-S3.
Sources
- Access logs are off by default, delivered to S3 as compressed files every 5 minutes, file naming, field order, best-effort delivery, and trailing fields added over time (accessed Nov 8, 2026)
- Bucket requirements (same Region, SSE-S3), the logdelivery.elasticloadbalancing.amazonaws.com bucket policy, the access_logs.s3 attributes, and the ELBAccessLogTestFile check (accessed Nov 8, 2026)
- ALB access, connection, and health check logs can be delivered to CloudWatch Logs, Firehose, or S3 from the Integrations tab (accessed Nov 8, 2026)
- Athena prerequisites and the list of ALB log table topics, including partition projection (accessed Nov 8, 2026)
- The alb_access_logs table columns and the advice to keep the trailing regex group for future fields (accessed Nov 8, 2026)
- Example queries for request counts by client, 5xx responses, and time ranges (accessed Nov 8, 2026)
- Connecting CloudWatch log groups to Sazabi through subscription filters from Settings > Log streams (accessed Nov 8, 2026)