2

I am building a serverless app with my SPA hosted on S3 and my APIs on lambda front ended by API gateway. Pretty standard stuff. Now I want my cloudfront distribution to sit in front of S3 and API gateway so that they are on same domain so as to prevent a CORS preflight request from my SPA.

The simplest way to turn cloudfront into non-caching mode I believe is to forward all headers. Unfortunately when I do this by setting the behavior I start getting cloudfront error 403. I am being forced to send cache control headers ( from my lambda 'Cache-Control': 'no-cache, no-store, must-revalidate') to prevent caching.

Request to API Gateway through cloudfront:

GET - https://dev.b*******e.in/dev/b******e/
:authority:dev.b******e.in
:method:GET
:path:/dev/b******e/
:scheme:https
accept:application/json, text/plain, */*
accept-encoding:gzip, deflate, sdch, br
accept-language:en-US,en;q=0.8
authorization:Bearer eyJmYWNlYm9vayI6eyJhY2Nlc3NfdG9rZW4iOiJFQUFZWGhyd1ZoTnNCQUlmOE5BUWU2ZnpwT2xDd2dUVVpBR1k0VmJZWkFMV0R4ekdYcEttek9NR0paQlNIdzdTeU5oRTloMTY3Nmh3bEZ4d2NpQ1laQnhDQ1IwUWZMWkFmVU1UMTRXOUxME1vWkQiLCJ0b2tlbl90eXBlIjoiYmVhcmVyIiwiZXhwaXJlc19pbiI6IjUxODM5NjcifX0=
cache-control:no-cache
dnt:1
pragma:no-cache
referer:https://dev.b******e.in/b***
user-agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36

Response - Status Code:403 
Response headers:
content-length:551
content-type:text/html
date:Fri, 21 Apr 2017 02:54:23 GMT
server:CloudFront
status:403
via:1.1 3336340a46390eb94ce8a4b00e4863fc.cloudfront.net (CloudFront), 1.1 efe8f585d51dfd5d8d354f74f0385a50.cloudfront.net (CloudFront), 1.1 84760d59a7d324aa7adc2bf9d8f7fd4b.cloudfront.net (CloudFront)
x-amz-cf-id:npw8LPJPvy1Ti_709BvUAHpZOdmOe1wsxm1jO7WdLACyzFESFGtMnQ==
x-cache:Error from cloudfront

Response Body:`

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
Bad request.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: 7BVUPh3HiZZGVKLWN9tMEz31gHzq762iPGHHW1fg2I7uNa87205P_Q==
</PRE>
<ADDRESS>
</ADDRESS>
</BODY></HTML>

`

How is everyone solving this problem? Below is my distribution setting. enter image description here

My behavior setting (This does not work): enter image description here

My behavior setting (This WORKS): enter image description here

Prabhat
  • 4,066
  • 4
  • 34
  • 41
  • Did you set the CNAME on the CloudFront distribution? – Bram Apr 15 '17 at 10:48
  • Yeah. I did add CNAME to the distribution. – Prabhat Apr 15 '17 at 11:41
  • Did you receive 403 on the request to API GW API call? Or s3 call? What was in the response body? – Balaji Apr 20 '17 at 17:49
  • Received 403 from cloudfront. Call did not reach API gateway. – Prabhat Apr 20 '17 at 19:43
  • What was the error message in the response body? Providing more details about the request and response will help us understand/investigate better. – Balaji Apr 21 '17 at 00:12
  • Please note that API GW uses CF distribution for your APIs. So, your request from the client is routed via CloudFront (owned by API GW) before it hits API Gateway. So, 403 is likely from API Gateway itself, which is masked by CF as "CF error". – Balaji Apr 21 '17 at 00:14
  • @Balaji You maybe right that error from cf of APIG maybe masked but does not look like should be since if I set the forward headers to "None" or "Whitelist (Authorization)" it just works fine . I have updated the question to include details of request and response and my behavior setting as well. Thanks for looking into this. Appreciate it – Prabhat Apr 21 '17 at 03:37
  • I am from API Gateway team. I tried to hit the endpoint you specified in the above request and that seems to respond with 200 status. I checked that the CloudFront distribution is still setup to forward all headers. So, I guess the issue is resolved. If not, please explain the issue. If yes, can you explain what fixed the issue? – Balaji Apr 23 '17 at 17:22
  • Thanks Balaji for looking into it. Current behavior that is working is not setup to forward all headers. To make it work I have done following. I have whitelisted authorization header. Also I am returning 'Cache-Control': 'no-cache, no-store, must-revalidate' headers to ensure that caching does not happen. API Gateway endpoint itself without cloudfront works fine. I am adding screenshot of working setup as well in question for reference. – Prabhat Apr 24 '17 at 02:17

2 Answers2

3

What has been killing me was the "Host" header. when you forward all the headers,"Host" header also gets forwarded to API gateway from cloudfront and it causes API gateway to fail. Check this link https://docs.aws.amazon.com/apigateway/api-reference/making-http-requests/ . Looks like the only solution to this currently is to whitelist the headers that you want, and send the cache-control headers from your lambda function.

Prabhat
  • 4,066
  • 4
  • 34
  • 41
1

I know my way is kinda hack but works on my side. Inspired by your finding that the "Host" header break API GW, I modify the request object using a Lambda@edge function to change the host header to API Gateway's host name. Lambda@edge is lambda function that can be triggered by CloudFront before it sends origin requests.

wenzx94
  • 11
  • 1