This Worker is adapted from the aws4fetch library. It aims to be the simplest possible solution for authenticating requests to S3 buckets from Cloudflare Workers using AWS Signature Version 4
Quickstart • Cache • Advanced
- Range requests
- Conditional requests (e.g.
if-none-match
) - CORS preflight requests
- Get your security credentials from AWS here: https://console.aws.amazon.com/iam/home?#/security_credentials. You'll need the access key ID and secret access key
- Go to https://dash.cloudflare.com/workers/view, select
Create a Worker
and name it - Paste the contents of index.js in the Worker body, then press
Save and Deploy
. If you need to modify the behavior offetch()
, skip to the end ofindex.js
- Press the back arrow and add the following environment variables (4 total):
ACCESS_KEY_ID [🔒 Encrypt]
,SECRET_ACCESS_KEY [🔒 Encrypt]
,S3_BUCKET_NAME
, andS3_REGION
For bucket name and region, here are three examples. In all cases - so long as the client requests
/20806827090258869800702155681/IMG_8799.jpg
- the signature will be valid:
# https://{BUCKET_NAME}.s3.{S3_REGION}.amazonaws.com/20806827090258869800702155681/IMG_8799.jpg
# https://s3-{S3_REGION}.amazonaws.com/{S3_BUCKET_NAME}/20806827090258869800702155681/IMG_8799.jpg
# https:/s3.{S3_REGION}.amazonaws.com/{S3_BUCKET_NAME}/20806827090258869800702155681/IMG_8799.jpg
# https://s3.amazonaws.com/{S3_BUCKET_NAME}/20806827090258869800702155681/IMG_8799.jpg (us-east-1 only)
- When you're done, this is what your configuration should look like:
- Finally, add a route under the zone/subdomain that you want to listen on for client requests, like
https://api.cflr.example.com/*
and apply the Worker you just created:
This Worker does not specify any cache settings. Please refer to Cloudflare's general cache documentation, the Cache API or the Advanced Configuration section below.
Note: if you have a more complex workflows or need to sign requests for other AWS services, please use aws4fetch, from which this Worker is adapted.
/**
* All examples should replace the default Worker method here:
* https://github.com/shagamemnon/s3workers/blob/ad74086c7b4d36b75b86f881d20ee4278839d8ec/index.js#L257
*/
/**
* Example #1
* Sign the request; don't specify any cache settings
*/
addEventListener('fetch', event => event.respondWith(handle(event.request)))
async function handle (request) {
if (request.method === 'OPTIONS') {
return new Response('', {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type,Authorization',
'Access-Control-Allow-Methods': 'GET,OPTIONS,POST',
'Access-Control-Max-Age': 86400
}
})
}
let signedRequest = await AwsClient.sign(request)
return fetch(signedRequest)
}
/**
* Example #2
* Sign the request URL only
* You can just pass the just the eyeball request URL for signing. Note that
* most headers will be removed prior to the fetch() as AWS will only accept
* headers that are ordered in a specific way.
*/
addEventListener('fetch', event => event.respondWith(handle(event.request)))
async function handle (request) {
//..
let signedRequest = await AwsClient.sign(request.url)
return fetch(signedRequest)
}
/**
* Example #3
* Cache subrequest using the cf options object
*/
addEventListener('fetch', event => event.respondWith(handle(event.request)))
async function handle (request) {
//..
let signedRequest = await AwsClient.sign(request.url)
return fetch(signedRequest, {
cf: {
cacheTtl: 3600,
cacheTtlByStatus: {
"200-299": 86400,
"404": 1,
"500-599": 0
},
cacheKey: request.url.split('?').pop()
}
})
}