AWS CloudFront URL Signature Utility
`aws-cloudfront-sign` is a JavaScript utility module designed to simplify the generation of signed URLs and signed cookies for Amazon CloudFront distributions. It addresses the historical gap where the official AWS SDK for JavaScript did not directly provide methods for CloudFront-specific URL signing, distinct from S3 signing. The package is currently stable at version 3.0.2, released in April 2026, with a release cadence that appears reactive to feature additions, bug fixes, and maintenance, rather than on a strict schedule. Key differentiators include its focused purpose on CloudFront signing, providing a straightforward API for both URL and cookie signing, and its compatibility with modern Node.js environments (v18+), including full TypeScript and ES Modules support since v3.0.0. While the AWS SDK for JavaScript now offers CloudFront signing capabilities, `aws-cloudfront-sign` remains a viable and actively maintained alternative.
Common errors
-
Access Denied
cause Signed URL/cookie has expired, or there's a significant clock drift between the server generating the signature and CloudFront.fixIncrease `expireTime` or ensure `expireTime` is set sufficiently far in the future. Verify server clock synchronization. Check CloudFront distribution settings and key pair configuration. -
Error: error:0906D06C:PEM routines:PEM_read_bio:no start line
cause The `privateKeyString` is malformed, missing the `-----BEGIN RSA PRIVATE KEY-----` or `-----END RSA PRIVATE KEY-----` headers, or newlines are incorrectly handled.fixEnsure the `privateKeyString` is the exact content of the `.pem` file, including all newlines. If loading from an environment variable, ensure `\n` characters are properly encoded/decoded. -
TypeError: getSignedUrl is not a function
cause Attempting to use CommonJS `require` syntax in a pure ESM project, or vice-versa, or incorrect named import.fixFor ES Modules (Node.js >=18, `type: "module"` in `package.json`), use `import { getSignedUrl } from 'aws-cloudfront-sign';`. For CommonJS, use `const { getSignedUrl } = require('aws-cloudfront-sign');`.
Warnings
- breaking The `expireTime` option changed its unit from seconds to milliseconds, a `Date` object, or a `moment` object.
- deprecated The `getSignedRTMPUrl` API is deprecated and its underlying technology (RTMP) has been discontinued by Amazon CloudFront.
- gotcha When providing `privateKeyString` directly, it must include the full PEM-encoded string with newline characters (`\n`) between sections.
- breaking Minimum Node.js engine requirement updated to version 18 or higher.
- gotcha The default `expireTime` for signed URLs and cookies was increased from 30 seconds to 30 minutes to mitigate 'Access Denied' errors caused by clock drift.
Install
-
npm install aws-cloudfront-sign -
yarn add aws-cloudfront-sign -
pnpm add aws-cloudfront-sign
Imports
- getSignedUrl
const getSignedUrl = require('aws-cloudfront-sign').getSignedUrl;import { getSignedUrl } from 'aws-cloudfront-sign'; - getSignedCookies
import getSignedCookies from 'aws-cloudfront-sign';
import { getSignedCookies } from 'aws-cloudfront-sign'; - SignatureOptions
import { SignatureOptions } from 'aws-cloudfront-sign/types';import type { SignatureOptions } from 'aws-cloudfront-sign';
Quickstart
import { getSignedUrl, getSignedCookies } from 'aws-cloudfront-sign';
import * as path from 'path';
import * as fs from 'fs';
// Replace with your actual CloudFront distribution URL
const cloudfrontUrl = 'https://d1234.cloudfront.net/path/to/my/asset.jpg';
// Your CloudFront key pair ID
const keypairId = process.env.CLOUDFRONT_KEY_PAIR_ID ?? 'APKAEIBAZAZAZAZAZAZA';
// Path to your private key file or the key content as a string
const privateKeyPath = process.env.CLOUDFRONT_PRIVATE_KEY_PATH ?? path.resolve(__dirname, 'pk-APKAEIBAZAZAZAZAZAZA.pem');
let privateKeyString: string;
try {
privateKeyString = fs.readFileSync(privateKeyPath, 'utf8');
} catch (error) {
console.error(`Error reading private key file at ${privateKeyPath}:`, error);
// Fallback for demonstration if file not found, but this should be valid in a real app
privateKeyString = process.env.CLOUDFRONT_PRIVATE_KEY_STRING ??
'-----BEGIN RSA PRIVATE KEY-----\n' +
'MIIJKAIBAAKCAgEAwGPMqEvxPYQIffDimM9t3A7Z4aBFAUvLiITzmHRc4UPwryJp\n' +
'EVi3C0sQQKBHlq2IOwrmqNiAk31/uh4...[truncated]...QIDAQABAoIBAB2zE\n' +
'-----END RSA PRIVATE KEY-----';
}
// Options for signing, including an expiration time (e.g., 1 hour from now)
const oneHourFromNow = Date.now() + 60 * 60 * 1000; // 1 hour in milliseconds
const options = {
keypairId: keypairId,
privateKeyString: privateKeyString,
expireTime: oneHourFromNow,
// ipRange: '192.168.1.0/24' // Optional: restrict access to a specific IP range
};
async function signCloudFrontResources() {
try {
// 1. Get a signed URL
const signedUrl = getSignedUrl(cloudfrontUrl, options);
console.log('Generated Signed URL:', signedUrl);
// 2. Get signed cookies
const signedCookies = getSignedCookies(cloudfrontUrl, options);
console.log('Generated Signed Cookies:', signedCookies);
// Example of how you might set cookies in a response (e.g., Express.js)
// res.cookie('CloudFront-Policy', signedCookies['CloudFront-Policy'], { httpOnly: true, secure: true });
// res.cookie('CloudFront-Signature', signedCookies['CloudFront-Signature'], { httpOnly: true, secure: true });
// res.cookie('CloudFront-Key-Pair-Id', signedCookies['CloudFront-Key-Pair-Id'], { httpOnly: true, secure: true });
} catch (error) {
console.error('Error signing CloudFront resources:', error);
}
}
signCloudFrontResources();