Unable to serve private CF images
Got a SvelteKit app, and I'm trying out CF images.
Upload was nice and easy, but actually serving the images is proving to be a bit of a nightmare
I've follow the docs here:
https://developers.cloudflare.com/images/cloudflare-images/serve-images/serve-private-images-using-signed-url-tokens/
It produces a good looking signature...
https://imagedelivery.net/p78SV55WKTCHZ1_ICRgIKg/43b26a16-265b-4dca-8516-f74e83572501/public?exp=1695126191&sig=875063554c4495f3fc173d87b9483e67e11af8df78a3fe57e52b01dd37eb0c5a
But visiting the link errors with:
ERROR 9425: Image access denied: check failed: Malformed data found: The URL signature does not match. Expecting SHA-256 HMAC of the path without domain nor query stringSo far I've tried troubleshooting: * Checked API token permissions (currently
Cloudflare Images:Read, Cloudflare Images:Edit, Account Analytics:Read, Stream:Edit, Stream:Read
)
* Have tried rolling new token, didn't make a difference
* Have verified that the HMAC signature is "correct"
* Using CLOUDFLARE_ACCOUNT_ID
instead of CLOUDFLARE_ACCOUNT_HASH
* Tried without exp
query param so it's just signing url.pathname
...but I've run out of ideas...
Any advice would be much appreciatedServe private images using signed URL tokens · Cloudflare Image Opt...
If an image is marked to require a signed URL, it cannot be accessed without a token unless it is being requested for a variant that is set to always …
5 Replies
Here's the code, 95% copied from the sample code linked above
Hello, did you find a solution to this problem, I'm facing this too and so far I haven't found any solution, I use the same function as in the example informing the private url of the image, but I get the same error.
const KEY = process.env.NEXT_PUBLIC_TOKEN_CLOUDFLARE;
const EXPIRATION = 60 * 60 * 24;
const bufferToHex = (buffer: ArrayBuffer): string =>
Array.from(new Uint8Array(buffer)).map(b => b.toString(16).padStart(2, '0')).join('');
async function generateSignedUrl(imageUrl: string): Promise<string> {
const encoder = new TextEncoder();
const secretKeyData = encoder.encode(KEY);
const key = await crypto.subtle.importKey( 'raw', secretKeyData, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'] ); const expiry = Math.floor(Date.now() / 1000) + EXPIRATION;
const url = new URL(imageUrl); url.searchParams.set('exp', expiry.toString()); const stringToSign = url.pathname; const mac = await crypto.subtle.sign('HMAC', key, encoder.encode(stringToSign)); const sig = bufferToHex(mac); url.searchParams.set('sig', sig); return url.toString(); } export { generateSignedUrl }; const imageUrl = "https://imagedelivery.net/0F1Nj_HMA0zNbUzFz7cUlw/658e1c6b-3efb-457c-8340-a15f48389401/public"; generateSignedUrl(imageUrl) .then(signedUrl => { console.log("URL: ", signedUrl); // Use o signedUrl conforme necessário, por exemplo, em um link de imagem }) .catch(error => { console.error("URL error: ", error); });
const key = await crypto.subtle.importKey( 'raw', secretKeyData, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'] ); const expiry = Math.floor(Date.now() / 1000) + EXPIRATION;
const url = new URL(imageUrl); url.searchParams.set('exp', expiry.toString()); const stringToSign = url.pathname; const mac = await crypto.subtle.sign('HMAC', key, encoder.encode(stringToSign)); const sig = bufferToHex(mac); url.searchParams.set('sig', sig); return url.toString(); } export { generateSignedUrl }; const imageUrl = "https://imagedelivery.net/0F1Nj_HMA0zNbUzFz7cUlw/658e1c6b-3efb-457c-8340-a15f48389401/public"; generateSignedUrl(imageUrl) .then(signedUrl => { console.log("URL: ", signedUrl); // Use o signedUrl conforme necessário, por exemplo, em um link de imagem }) .catch(error => { console.error("URL error: ", error); });
@valdeir3194 I did get it working - but I can't remember what the specific issue was when I raised this issue
Let me see if I can find the code for you that I am currently using
Let me know if that works for you, my phone thinks it's
.ts
is a video file format for some reason