I have a statically generated site that I have uploaded to S3 and surface through a Cloudfront distribution - it has a root index.html file and several statically generated subpages (e.g. /donate/index.html). I have configured the bucket to act as a static host with index.html as an index document. And I can visit these pages by visiting the S3 prefix directly, e.g. /donate/ correctly displays the html in /donate/index.html.
Here's the issue - when I link to /donate?some=query¶m=here S3 automatically 302 redirects these pages without a trailing slash to page with the trailing slash, removing the query parameters which are used by my client-side javascript for lightly tracking state.
For example /donate?some=query¶m=here 302s to /donate/ and loses that context, which makes it hard for folks building URLs because they must remember to include the trailing slash (HTTP 302 is also a bad http response if you want web crawlers to remember the new route).
Here's what did not work:
Here's what finally cracked it - creating a Viewer Request Cloudfront Function to manually check if the path contains neither a file extension nor a trailing slash and adding one in those cases.
function handler (event) {
var request = event.request;
var uri = request.uri;
if( !uri.includes('.') && !uri.endsWith('/') ) {
request.uri = uri + '/'
}
return request;
};
Create this function in Cloudfront (Cloudfront > Functions > Create Function) and then attach it to your distribution's behavior (Cloudfront > Distribution > Behavior > Edit > Viewer Request > Cloudfront Functions).
Make sure you invalidate the site after the distribution has finished initializing with the new settings and test the new route via CURL - you should see the following behavior change:
# BEFORE:
curl -v --location http://example.com/donate?please=keep > /dev/null
> GET /donate?please=keep
> RESP 302
> GET /donate/
> RESP 200
# AFTER
curl -v --location http://example.com/donate?please=keep > /dev/null
> GET /donate?please=keep
> RESP 200
Is there another way? Will I regret paying for every Cloudfront function invocation? Is there a way to make this happen with purely Cloudfront or S3 policy changes?