I have a single page application and I'm trying to prevent clickjacking by adding X-Frame-Options header to the HTML responses. My website is hosted on S3 through CloudFront.
The CloudFront distribution is configured to send index.html
by default:
Default root object
index.html
In the Error Pages section I configured 404 page to also point to the index.html
. This way all URLs that are not in S3 return the default HTML, i.e. /login
, /admin
etc.
Update The 403 status code is also configured:
Then I have created a CloudFront function as described here and assigned it to the Viewer response:
function handler(event) {
var response = event.response;
var headers = response.headers;
headers['x-frame-options'] = {value: 'DENY'};
return response;
}
This works, but only for /
:
curl -v https://<MYSITE.com>
....
< x-frame-options: DENY
For other URLs it doesn't work - the x-frame-options header is missing:
curl -v https://<MYSITE.com>/login
....
< x-cache: Error from cloudfront
My question is - why my cloudfront function does not append a header in the error response, and what can I do to add it?
I understand that your questions are:
/
?Please refer to the responses below:
Default Root Object
[1] (e.g.index.html
) which returning the object when a user requests the root URL. When CloudFront returns the object with 200 ok
, the CloudFront Function will be invoked on the viewer response
event.s3:ListBucket
permissions in your S3 bucket policy(e.g. OAI). As the result, you will get Access Denied
(403) errors for missing objects instead of 404 Not Found errors. Namely, the Error Pages
you have configured isn't applied to this case, and the CloudFront Function won't be invoked because the HTTP status code is higher than 399[2].[Updated]
Suggestion:
viewer response
events when the origin returns HTTP status code 400 or higher. However, Lambda@Edge functions for origin response
events are invoked for all origin responses. In this senario, I'll suggest that we should use Lambda@Edge instead of CloudFront Functions.exports.handler = async (event, context) => {
const response = event.Records[0].cf.response;
const headers = response.headers;
headers['x-frame-options'] = [{
key: 'X-Frame-Options',
value: 'DENY',
}];
return response;
};
# PATH: `/`
$ curl -sSL -D - https://dxxxxxxx.cloudfront.net/
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 12
Connection: keep-alive
ETag: "e59ff97941044f85df5297e1c302d260"
___snipped___
Server: AmazonS3
X-Frame-Options: DENY
___snipped___
# PATH: `/login`
$ curl -sSL -D - https://dxxxxxxx.cloudfront.net/login
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 12
Connection: keep-alive
ETag: "e59ff97941044f85df5297e1c302d260"
___snipped___
Server: AmazonS3
X-Frame-Options: DENY
___snipped___