I have an ALB with a Lambda as a target. The lambda is a simple Python one and the only thing it does, is a return of a hardcoded dict. The response is a valid response for ALB and includes Etag header:

import json
def lambda_handler(event, context):
    result = {
        'statusCode': 200,
        'statusDescription': '200 OK',
        'isBase64Encoded': False,
        'body': event['body'],
        'headers': {
            'Etag': 'someetag-1',
            'Content-Type': 'application/json'
    return result

When I send a POST request, I get a response with 200 status code:

$ curl -X POST \
>   http://test-alb-000000000000.us-east-1.elb.amazonaws.com \
>   -H 'Content-Type: application/json' \
>   -d '{"some": "body"}'
{"some": "body"}

However, when I provide one of If-Unmodified-Since, If-Match headers I receive 412 Precondition Failed:

$ curl -X POST \
>   http://test-alb-000000000000.us-east-1.elb.amazonaws.com \
>   -H 'Content-Type: application/json' \
>   -H 'If-Match: someetag' \
>   -d '{"some": "body"}'
<head><title>412 Precondition Failed</title></head>
<body bgcolor="white">
<center><h1>412 Precondition Failed</h1></center>

From lambda logs I can see however that the lambda was executed and finished properly for both requests:

START RequestId: 91ea1dd7-9ad4-430d-b9d0-36d6d857455f Version: $LATEST
{'requestContext': {'elb': {'targetGroupArn': 'arn:aws:elasticloadbalancing:us-east-1:000000000000:targetgroup/TargetGroupName/f27b55fbd94d7be6'}}, 'httpMethod': 'POST', 'path': '/', 'queryStringParameters': {}, 'headers': {'accept': '*/*', 'content-length': '16', 'content-type': 'application/json', 'host': 'test-alb-000000000000.us-east-1.elb.amazonaws.com', 'user-agent': 'curl/7.54.0', 'x-amzn-trace-id': 'Root=1-7c349c62-54934927ad90ded6e9cb166e', 'x-forwarded-for': '', 'x-forwarded-port': '80', 'x-forwarded-proto': 'http'}, 'body': '{"some": "body"}', 'isBase64Encoded': False}
{'statusCode': 200, 'statusDescription': '200 OK', 'isBase64Encoded': False, 'body': '{"some": "body"}', 'headers': {'Etag': 'someetag-1', 'Content-Type': 'application/json'}}
END RequestId: 91ea1dd7-9ad4-430d-b9d0-36d6d857455f
REPORT RequestId: 91ea1dd7-9ad4-430d-b9d0-36d6d857455f Duration: 5.45 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 55 MB Init Duration: 113.41 ms
XRAY TraceId: 1-5d832f3d-54934927ad90ded6e9cb166e SegmentId: 458533393206659a Sampled: false

START RequestId: d2c41e28-8ffd-4a20-b9ee-97aac9a2a7e4 Version: $LATEST
{'requestContext': {'elb': {'targetGroupArn': 'arn:aws:elasticloadbalancing:us-east-1:000000000000:targetgroup/TargetGroupName/f27b55fbd94d7be6'}}, 'httpMethod': 'POST', 'path': '/', 'queryStringParameters': {}, 'headers': {'accept': '*/*', 'content-length': '16', 'content-type': 'application/json', 'host': 'test-alb-000000000000.us-east-1.elb.amazonaws.com', 'if-match': 'someetag', 'user-agent': 'curl/7.54.0', 'x-amzn-trace-id': 'Root=1-5d832f44-54934927ad90ded6e9cb166e', 'x-forwarded-for': '', 'x-forwarded-port': '80', 'x-forwarded-proto': 'http'}, 'body': '{"some": "body"}', 'isBase64Encoded': False}
{'statusCode': 200, 'statusDescription': '200 OK', 'isBase64Encoded': False, 'body': '{"some": "body"}', 'headers': {'Etag': 'someetag-1', 'Content-Type': 'application/json'}}
END RequestId: d2c41e28-8ffd-4a20-b9ee-97aac9a2a7e4
REPORT RequestId: d2c41e28-8ffd-4a20-b9ee-97aac9a2a7e4 Duration: 7.13 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 56 MB
XRAY TraceId: 1-5d832f44-54934927ad90ded6e9cb166e SegmentId: 7a6071cb33eb8af2 Sampled: false

ALB access logs for a request without If-Match header:

http 2019-09-19T07:32:06.546111Z app/test-alb/4c92127c7308 - 0.007 0.277 0.000 200 200 180 186 "POST http://test-alb-00000000.us-east-1.elb.amazonaws.com:80/ HTTP/1.1" "curl/7.54.0" - - arn:aws:elasticloadbalancing:us-east-1:000000000000:targetgroup/TargetGroupName/f27b55fbd94d7be6 "Root=1-7c349c62-54934927ad90ded6e9cb166e" "-" "-" 0 2019-09-19T07:32:06.262000Z "forward" "-" "-"

ALB access logs for a request with If-Match header:

http 2019-09-19T07:32:14.645790Z app/test-alb/4c92127c7308 - 0.008 0.059 0.000 412 - 200 317 "POST http://test-alb-00000000.us-east-1.elb.amazonaws.com:80/ HTTP/1.1" "curl/7.54.0" - - - "-" "-" "-" - 2019-09-19T07:32:14.579000Z "-" "-" "-"

The same 412 Precondition Failed response is returned any time I use If-Unmodified-Since or If-Match. The value of those headers doesn't matter. Also, it doesn't matter whether the lambda returns Etag, Last-Modified headers or none of them. Basically, any time one of conditional headers If-Unmodified-Since or If-Match is present, the ALB returns 412 Precondition Failed.

How I can properly use If-Unmodified-Since and If-Match headers with ALB and Lambda? Also, I don't understand why the access logs doesn't say that the request was forwarded to the target when actually it was forwarded (Lambda was executed and finished properly). This problem does not occur with API Gateway but I need to use ALB in my case.

  • You are performing a conditional request against a resource that lacks both a Last-Modified and an ETag... so conditional requests in the real world should never happen. The behavior seems buggy, but what happens if your generated response actually contains the necessary headers? – Michael - sqlbot Sep 18 at 9:57
  • I thought about it and I checked it already - behavior is the same if the lambda returns Last-Modified header. Also, if I have ALB, it should just route the requests, not act on conditional headers I think. It's not a cache, it's not Cloudfront. – Pawel Sep 18 at 11:11
  • I am inclined to agree that the balancer should stay out of the way. RFC-7232 does not seem to suggest that 412 is a sensible response for GET or HEAD. But... in order to properly replicate the issue and offer assistance, I'd suggest we need to eliminate the variables and see (1) the code for a Lambda function that correctly returns Last-Modified and/or ETag as appropriate, with values that are syntactically correct... and an example curl request that replicates the bad behavior. – Michael - sqlbot Sep 18 at 13:02
  • I did that already before and it didn't work at all. But I agree, I will update the question here with a code that returns a header and curl examples. – Pawel Sep 18 at 13:52
  • @Michael-sqlbot, I've updated the example as you suggested. It does not matter much though, ALB is returning the same response anytime Last-Modified and/or ETag are present. – Pawel Sep 19 at 8:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.