Modifying CloudFront Origin Request for S3 - amazon-s3

I have two S3 buckets behind a CloudFront distribution.
One bucket is the default behavior origin; one is an additional behavior under a specific path pattern.
The problem is that CloudFront uses the whole path to request the bucket object, which requires the exact path to exist as part of the object name in the bucket.
Path pattern auth/* requires an auth/index.html object if I request .../auth/index.html.
If I change the path, I would have to change all the object names in the bucket.
Is there a way around this?

Thanks to a comment, I found a solution with Lambda#Edge.
If you used the path pattern auth/* in a CloudFront behavior so that all URLs starting with auth/ are routed to a specific S3 bucket, and you want to save all your files in that bucket without an auth/ prefix. Then you need to add this edge Lambda to your CloudFront behavior:
const prefix = "auth/"
exports.handler = async (e) => {
const { request } = e.Records.pop().cf;
if (request.uri.contains(prefix))
request.uri = request.uri.replace(prefix, "");
return request;
});
The event type is origin request.
On a request to auth/index.html CloudFront will fetch index.html from your associated bucket and not auth/index.html
Since origin requests are only sent to the S3 bucket when a cache miss happens, this should be a quite cheap solution.

Related

Dynamically override content-type response header in AWS CloudFront

I'm using AWS S3 and CloudFront to distribute content.
My files stored in S3 have binary/octet-stream type but in fact they can be images or documents.
When generating S3 URLs, I can override the response content type with a parameter like this: https://my-bucket.s3.eu-central-1.amazonaws.com/foo/bar?[...]&response-content-type=image/jpeg
Is there a way to do it with CloudFront (with the same S3 bucket as origin)?
I tried adding the same parameter -- it's ignored.
I can create a response headers policy and add a custom header overriding origin, but as far as I can see, it'll be the same for all files whereas I need to control it with a request parameter.

Point subfolder in S3 bucket to *.buildmywebsite domain [duplicate]

I've got a pretty specific problem here, we've got a system that we already have and maintain, the system involves using subdomains to route people to specific apps.
on a traditional server that goes like follows; we have a wildcard subdomain, *.domain.com that routes to nginx and serves up a folder
so myapp.domain.com > nginx > serves up myapp app folder > myapp folder contains a static site
I'm trying to migrate this in some way to AWS, I basically need to do a similar thing in AWS, I toyed with the idea of putting each static app into an s3 bucket and then the wildcard domain in route 53 but i'm unsure how s3 would know which folder to serve up as that functionality isn't part of route 53
Anyone have any suggestions?
Thanks for all your help
CloudFront + Lambda#Edge + S3 can do this "serverless."
Lambda#Edge is a CloudFront enhancement that allows attributes of requests and responses to be represented and manipulated as simple JavaScript objects. Triggers can be provisioned to fire during request processing, either before the cache is checked ("viewer request" trigger) or before the request proceeds to the back-end ("origin server", an S3 web site hosting endpoint, in this case) following a cache miss ("origin request" trigger)... or during response processing, after the response is received from the origin but before it is considered for storing in the CloudFront cache ("origin response" trigger), or when finalizing the response to the browser ("viewer response" trigger). Response triggers can also examine the original request object.
The following snippet is something I originally posted at the AWS Forums. It is an Origin Request trigger which compares the original hostname to your pattern (e.g. the domain must match *.example.com) and if it does, the hostname prefix subdomain-here.example.com is request is served from a folder named for the subdomain.
lol.example.com/cat.jpg -> my-bucket/lol/cat.jpg
funny-pics.example.com/cat.jpg -> my-bucket/funny-pics/cat.jpg
In this way, static content from as many subdomains as you like can all be served from a single bucket.
In order to access the original incoming Host header, CloudFront needs to be configured to whitelist the Host header for forwarding to the origin even though the net result of the Lambda function's execution will be to modify that value before the origin acually sees it.
The code is actually very simple -- most of the following is explanatory comments.
'use strict';
// if the end of incoming Host header matches this string,
// strip this part and prepend the remaining characters onto the request path,
// along with a new leading slash (otherwise, the request will be handled
// with an unmodified path, at the root of the bucket)
const remove_suffix = '.example.com';
// provide the correct origin hostname here so that we send the correct
// Host header to the S3 website endpoint
const origin_hostname = 'example-bucket.s3-website.us-east-2.amazonaws.com'; // see comments, below
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
const host_header = headers.host[0].value;
if(host_header.endsWith(remove_suffix))
{
// prepend '/' + the subdomain onto the existing request path ("uri")
request.uri = '/' + host_header.substring(0,host_header.length - remove_suffix.length) + request.uri;
}
// fix the host header so that S3 understands the request
headers.host[0].value = origin_hostname;
// return control to CloudFront with the modified request
return callback(null,request);
};
Note that index documents and redirects from S3 may also require an Origin Response trigger to normalize the Location header against the original request. This will depend on exactly which S3 website features you use. But the above is a working example that illustrates the general idea.
Note that const origin_hostname needs to be set to the bucket's endpoint hostname as configured in the CloudFront origin settings. In this example, the bucket is in us-east-2 with the web site hosting feature active.
Create a Cloudfront distribution
Add all the Alternate CNAMEs records in the cloudfront distribution
Add a custom origin as the EC2 server.
Set behaviours as per your requirements.
Configure nginx virtualhosts in the server to route to specific folders.

Hugo multilingual site deploy to aws s3 error "AccessDenied"

I'm hosting a static website on AWS S3, served through AWS Cloudfront. Site is build with Hugo in multilingual mode. So it redirects from index page "/" to default lang index page url /en . On this page I get error access denied. When I manually type URL /en/index.html page loaded normally.
How should I setup AWS bucket or Hugo to show page properly?
Find answer in this post
CloudFront + S3 Website: "The specified key does not exist" when an implicit index document should be displayed
I need to use the bucket's web site hosting endpoint as the origin domain name in Cloudfront setup
One way of doing it is to follow the link shared by #qwelp,whereby the solution states that if you are using S3 REST API endpoint as opposed to the static website hosting, you would need to change the origin of your CloudFront from "S3" to "custom" origin.
The other way to solve this is implement a lambda edge function that redirects all user requests that requests files in hugo sub-directories. As quoted inAWS Knowledge Center here ;
use Lambda#Edge to be able to use CloudFront with an S3 origin access
identity and serve a default root object on subdirectory URLs.
From AWS Knowledge Center
Why isn't CloudFront returning my default root object from a
subdirectory? Issue Why isn't Amazon CloudFront returning my default
root object from a subfolder or subdirectory? Resolution The default
root object feature for CloudFront supports only the root of the
origin that your distribution points to. CloudFront doesn't return
default root objects in subdirectories. For more information, see
Specifying a Default Root Object.
If your CloudFront distribution must return the default root object
from a subfolder or subdirectory, you can integrate Lambda#Edge with
your distribution. For an example configuration, see Implementing
Default Directory Indexes in Amazon S3-backed Amazon CloudFront
Origins Using Lambda#Edge.
Important: You'll be charged an additional fee when you use Lambda#Edge. For more information, see Lambda#Edge Pricing Details.
As stated in AWS Knowledge Center here
"In this example, you use the compute power at the CloudFront edge to inspect the request as it’s coming in from the client. Then re-write the request so that CloudFront requests a default index object (index.html in this case) for any request URI that ends in ‘/’."
The Lambda Edge Function in JS:
'use strict';
exports.handler = (event, context, callback) => {
// Extract the request from the CloudFront event that is sent to Lambda#Edge
var request = event.Records[0].cf.request;
// Extract the URI from the request
var olduri = request.uri;
// Match any '/' that occurs at the end of a URI. Replace it with a default index
var newuri = olduri.replace(/\/$/, '\/index.html');
// Log the URI as received by CloudFront and the new URI to be used to fetch from origin
console.log("Old URI: " + olduri);
console.log("New URI: " + newuri);
// Replace the received URI with the URI that includes the index page
request.uri = newuri;
// Return to CloudFront
return callback(null, request);
};
Ensure that you gave the Lambda#Edge function IAM permissions
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:*:*:*"
]
}
]
}
I suffered the same problem and I ended up setting up my S3 bucket as public, I didn't want to pay an extra Lambda for handling this.
Here is a post explaining the situation.

Direct CloudFront to Specific Bucket Folder if Path Elements Exists

I have a CloudFront distribution in front of a S3 static website.
I want all traffic where first path element is profile to be directed to one file in my bucket.
For example, for a GET request to https://example.com/profile/12345, I want CloudFront to return S3 object s3://<bucket_name>/profile/index.html.
My goal is that I would handle dynamic content for profile/12345 using frontend JS and my API.

Resolve S3 static page from custom path with CloudFront

I've got an S3 bucket configured to host a static website, and if I browse to the bucket URL it shows the index.html file. I also have a CloudFront distribution to show another S3 bucket under a custom domain. Is there any chance I could configure CloudFront to serve one bucket from the root and another from a custom path? So:
mydomain.com -> bucket1/index.html
mydomain.come/some-path -> bucket2/index.html
I already created an origin for the bucket and set up a path pattern for it and some-path, but I'm getting 403 Forbidden, even though if I browse to the origin directly I can see the webpage.
This configuration works fine, but it requires that the object in bucket2 be located at some-path/index.html inside bucket2.
The path pattern you configured in the cache behavior is still part of the request path, so it is forwarded to the origin.
CloudFront does not support removing part of the request path before forwarding the request to the origin.