Skip to content

Changelog

New updates and improvements at Cloudflare.

View transformation analytics in Images

You can now view account-level analytics for your Images transformation usage.

Go to Images & Stream > Transformations > Analytics to view sampled estimates of image transformation request traffic, including:

  • Requests by source, split between URL-based transformations and Images binding transformations
  • Top zones, transformation configurations, and origin hosts for URL-based requests
  • Top Worker scripts for Images binding requests

Use these analytics to identify the zones, configurations, origins, and Workers generating the most image transformation requests.

New in Images: text rasterization and updates to the binding

We've added more ways to manage and manipulate images with the Images binding. Here's what's new:

Render text into an image. Output a string of text into its own image or draw it over another image.

  • Use the .text() method to rasterize text with the Images binding.
  • Style content using the font, size, and color options.
  • The draw array in cf.image now accepts a text key.

Manage hosted images without an API token.

  • Metadata filtering: Pass filter.metadata to .list() to return images by custom metadata. Match a bounded range by setting two operators in one condition, for example, priority: { gte: 2, lte: 5 }.
  • Server-side signing: Get a signed URL for a private image with .signedUrl().
  • User uploads: Create a Direct Creator Upload link with .createDirectUpload() so that a client can upload an image to your storage.

Set headers in a single call.

  • Pass a headers option to .response() to set headers without rebuilding the Response.
  • Content-Type is always taken from the optimized image and can't be overridden by a specified header.
  • Set Cache-Control with Workers Cache to cache your optimized image at the edge.

For more information, refer to Optimize with Workers, Draw overlays and watermarks, and Manage hosted images with Workers.

Rotate Stream broadcast keys for live inputs

You can now rotate the broadcast credentials for a Stream live input without changing the live input identifier.

Use key rotation when live input credentials may have been shared with the wrong audience, exposed in client code or a screenshare, or need to be refreshed as part of your security process. Rotating keys revokes the old credentials, disconnects broadcasts using stale credentials, and returns refreshed credentials in the API response.

To rotate keys for a live input, make a POST request to the rotate_keys endpoint:

curl --request POST \
https://api.cloudflare.com/client/v4/accounts/{account_id}/stream/live_inputs/{live_input_identifier}/rotate_keys \
--header "Authorization: Bearer <API_TOKEN>"

Live input responses now also include keysRotatedAt, which indicates when the live input keys were last rotated. This field is omitted for live inputs whose keys have never been rotated.

For endpoint details, refer to Rotate keys for a live input. For usage guidance, refer to Manage live inputs.

Images binding is now billed per unique transformation

The Images binding is now billed per unique transformation, matching the model already used for URL-based transformations. Repeat requests for the same combination of source image and parameters within the same calendar month are counted only once.

Previously, every call to the binding counted as a separate transformation regardless of whether the image or parameters were unique. With this change, you can call the binding on hot paths without paying for each individual request.

Calls to .info() are no longer billed.

For more information, refer to Images pricing and the Images binding documentation.

New optimization features in Images

These updates introduce new features for optimizing and manipulating with Images:

  • New composite option: Control how overlays are blended with the base image.
  • Percentage widths: Set the dimensions of an overlay as a fraction of the dimensions of the base image.
  • New fit modes: Use aspect-crop to always preserve the target aspect ratio or scale-up to always enlarge images.
  • New upscale parameter: Apply AI upscaling to produce sharper, more detailed results when enlarging images.

Manage hosted images with the Images binding

Use the Images binding to upload, list, retrieve, update, and delete images stored in Images directly from your Worker without managing API tokens or making HTTP requests.

The env.IMAGES.hosted namespace supports the following storage and management operations:

For example, you can upload an image from a request body and return its metadata:

const image = await env.IMAGES.hosted.upload(request.body, {
	filename: "upload.jpg",
	metadata: { source: "worker" },
});

return Response.json(image);

Or retrieve and serve the original bytes of a hosted image:

const bytes = await env.IMAGES.hosted.image("IMAGE_ID").bytes();
return new Response(bytes);

For more information, refer to the Images binding.

Transformation flows in Images

Custom flow configuration panel

Flows are automated rules that pair conditions (such as file extension, URL path, or query parameter) with parameters. Set up a flow to automatically apply image optimization to matching requests on your zone without writing code or changing URLs.

There are two modes for transformation flows:

  • Provider flows — Migrate from another image optimization service. Your existing URLs continue to work while Cloudflare rewrites provider-specific parameters to their Cloudflare equivalents. Currently, Cloudflare supports provider flows for Fastly Image Optimizer.
  • Custom flows — Define your own conditions and actions for use cases like automatic format conversion, responsive sizing with width=auto, or directory-based optimization.

To get started, go to Images > Transformations > Automation in the Cloudflare dashboard ↗︎.

Learn more about transformation flows.

Introducing Stream Bindings for Workers

You can now interact with your Stream video library using new bindings for Workers! This allows customers to upload content to Stream, provision direct uploads, manage videos, and generate signed URLs from a Worker without making authenticated API calls. We're excited to bring Stream and Workers closer together to empower more programmatic pipelines, tighter integrations, and support generative AI and inference workloads.

Use the Stream binding when you want to:

  • Upload videos from URLs or create basic direct upload links for end users
  • Generate signed playback tokens without managing signing keys
  • Manage video metadata, captions, downloads, and watermarks
  • Build video pipelines entirely within Workers

To get started, add the Stream binding to your Wrangler configuration:

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "stream": {
    "binding": "STREAM"
  }
}
[stream]
binding = "STREAM"

Generate a video with AI and upload directly to Stream or send a URL of a file you already have:

const aiResponse = await env.AI.run(
	"google/veo-3.1",
	{
		prompt: "A dog walking next to a river",
		duration: "10s",
		aspect_ratio: "16:9",
		resolution: "1080p",
		generate_audio: true,
	},
	{
		gateway: { id: "experiments" },
	},
);

// Veo will return a URL of the generated asset.
const videoUrl = aiResponse.result.video;

// Alternative option: a video of the Austin Office mobile
// const videoUrl = 'https://pub-d9fcbc1abcd244c1821f38b99017347f.r2.dev/aus-mobile.mp4';

// Upload to Stream by providing a URL
const streamVideo = await env.STREAM.upload(videoUrl);

// The streamVideo response will include the video ID, playback and manifest
// URLs, and other information, just like the REST API.
const aiResponse = await env.AI.run(
	'google/veo-3.1',
	{
		prompt: 'A dog walking next to a river',
		duration: '10s',
		aspect_ratio: '16:9',
		resolution: '1080p',
		generate_audio: true,
	},
	{
		gateway: { id: 'experiments' },
	},
);

// Veo will return a URL of the generated asset.
const videoUrl = aiResponse.result.video;

// Alternative option: a video of the Austin Office mobile
// const videoUrl = 'https://pub-d9fcbc1abcd244c1821f38b99017347f.r2.dev/aus-mobile.mp4';

// Upload to Stream by providing a URL
const streamVideo = await env.STREAM.upload(videoUrl);

// The streamVideo response will include the video ID, playback and manifest
// URLs, and other information, just like the REST API.

Generate a signed URL without using a signing key or an API call:

const video_id = "ce800be43a9772f4bb02f35b860fb516";
const token = await env.STREAM.video(video_id).generateToken();

// Use the "token" in an iframe embed code, manifest URL, or thumbnail:
const embedUrl = `https://customer-igynxd2rwhmuoxw8.cloudflarestream.com/${token}/iframe`;
const video_id = 'ce800be43a9772f4bb02f35b860fb516';
const token = await env.STREAM.video(video_id).generateToken();

// Use the "token" in an iframe embed code, manifest URL, or thumbnail:
const embedUrl = `https://customer-igynxd2rwhmuoxw8.cloudflarestream.com/${token}/iframe`;

Get and set video properties easily:

const video_id = "46c8b7f480d410840758c1cb14a72e47";
const result = await env.STREAM.video(video_id).details();

await env.STREAM.video(video_id).update({
	meta: { name: "sample video" },
});
const video_id = '46c8b7f480d410840758c1cb14a72e47';
const result = await env.STREAM.video(video_id).details();

await env.STREAM.video(video_id).update({
  meta: { name: 'sample video' }
});

For setup instructions and the full API reference, refer to Bind to Workers API.

Get started with your Agent

Add a binding for Cloudflare Stream (env.STREAM). On the watch page, use the Stream binding to get info based on the ID, and leverage video.meta.name as the page title.

Media Transformations binding for Workers

You can now use a Workers binding to transform videos with Media Transformations. This allows you to resize, crop, extract frames, and extract audio from videos stored anywhere, even in private locations like R2 buckets.

The Media Transformations binding is useful when you want to:

  • Transform videos stored in private or protected sources
  • Optimize videos and store the output directly back to R2 for re-use
  • Extract still frames for classification or description with Workers AI
  • Extract audio tracks for transcription using Workers AI

To get started, add the Media binding to your Wrangler configuration:

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "media": {
    "binding": "MEDIA"
  }
}
[media]
binding = "MEDIA"

Then use the binding in your Worker to transform videos:

export default {
	async fetch(request, env) {
		const video = await env.R2_BUCKET.get("input.mp4");

		const result = env.MEDIA.input(video.body)
			.transform({ width: 480, height: 270 })
			.output({ mode: "video", duration: "5s" });

		return await result.response();
	},
};
export default {
	async fetch(request, env) {
		const video = await env.R2_BUCKET.get("input.mp4");

		const result = env.MEDIA.input(video.body)
			.transform({ width: 480, height: 270 })
			.output({ mode: "video", duration: "5s" });

		return await result.response();
	},
};

Output modes include video for optimized MP4 clips, frame for still images, spritesheet for multiple frames, and audio for M4A extraction.

For more information, refer to the Media Transformations binding documentation.

Stream live inputs can now be disabled and enabled

You can now disable a live input to reject incoming RTMPS and SRT connections. When a live input is disabled, any broadcast attempts will fail to connect.

This gives you more control over your live inputs:

  • Temporarily pause an input without deleting it
  • Programmatically end creator broadcasts
  • Prevent new broadcasts from starting on a specific input

To disable a live input via the API, set the enabled property to false:

curl --request PUT \
https://api.cloudflare.com/client/v4/accounts/{account_id}/stream/live_inputs/{input_id} \
--header "Authorization: Bearer <API_TOKEN>" \
--data '{"enabled": false}'

You can also disable or enable a live input from the Live inputs list page or the live input detail page in the Dashboard.

All existing live inputs remain enabled by default. For more information, refer to Start a live stream.

Introducing observability and metrics for Stream Live Inputs

New information about broadcast metrics and events is now available in Cloudflare Stream in the Live Input details of the Dashboard.

Live Input details showing metrics

You can now easily understand broadcast-side health and performance with new observability, which can help when troubleshooting common issues, particularly for new customers who are just getting started, and platform customers who may have limited visibility into how their end-users configure their encoders.

To get started, start a live stream (just getting started?), then visit the Live Input details page in Dash.

See our new live Troubleshooting guide to learn what these metrics mean and how to use them to address common broadcast issues.

Audio mode for Media Transformations

We now support audio mode! Use this feature to extract audio from a source video, outputting an M4A file to use in downstream workflows like AI inference, content moderation, or transcription.

For example,

Example URLtext
https://example.com/cdn-cgi/media/<OPTIONS>/<SOURCE-VIDEO>
https://example.com/cdn-cgi/media/mode=audio,time=3s,duration=60s/<input video with diction>

For more information, learn about Transforming Videos.

Introducing Origin Restrictions for Media Transformations

We are adding source origin restrictions to the Media Transformations beta. This allows customers to restrict what sources can be used to fetch images and video for transformations. This feature is the same as --- and uses the same settings as --- Image Transformations sources.

When transformations is first enabled, the default setting only allows transformations on images and media from the same website or domain being used to make the transformation request. In other words, by default, requests to example.com/cdn-cgi/media can only reference originals on example.com.

Enable allowed origins from the Cloudflare dashboard

Adding access to other sources, or allowing any source, is easy to do in the Transformations tab under Stream. Click each domain enabled for Transformations and set its sources list to match the needs of your content. The user making this change will need permission to edit zone settings.

For more information, learn about Transforming Videos.

Signed URLs and Infrastructure Improvements on Stream Live WebRTC Beta

Cloudflare Stream has completed an infrastructure upgrade for our Live WebRTC beta support which brings increased scalability and improved playback performance to all customers. WebRTC allows broadcasting directly from a browser (or supported WHIP client) with ultra-low latency to tens of thousands of concurrent viewers across the globe.

Additionally, as part of this upgrade, the WebRTC beta now supports Signed URLs to protect playback, just like our standard live stream options (HLS/DASH).

For more information, learn about the Stream Live WebRTC beta.

Introducing Media Transformations from Cloudflare Stream

Today, we are thrilled to announce Media Transformations, a new service that brings the magic of Image Transformations to short-form video files, wherever they are stored!

For customers with a huge volume of short video — generative AI output, e-commerce product videos, social media clips, or short marketing content — uploading those assets to Stream is not always practical. Sometimes, the greatest friction to getting started was the thought of all that migrating. Customers want a simpler solution that retains their current storage strategy to deliver small, optimized MP4 files. Now you can do that with Media Transformations.

To transform a video or image, enable transformations for your zone, then make a simple request with a specially formatted URL. The result is an MP4 that can be used in an HTML video element without a player library. If your zone already has Image Transformations enabled, then it is ready to optimize videos with Media Transformations, too.

URL formattext
https://example.com/cdn-cgi/media/<OPTIONS>/<SOURCE-VIDEO>

For example, we have a short video of the mobile in Austin's office. The original is nearly 30 megabytes and wider than necessary for this layout. Consider a simple width adjustment:

Example URLtext
https://example.com/cdn-cgi/media/width=640/<SOURCE-VIDEO>
https://developers.cloudflare.com/cdn-cgi/media/width=640/https://middlecache.ced.cloudflare.com/v1/aus-mobile/aus-mobile.mp4

The result is less than 3 megabytes, properly sized, and delivered dynamically so that customers do not have to manage the creation and storage of these transformed assets.

For more information, learn about Transforming Videos.

Bind the Images API to your Worker

You can now interact with the Images API directly in your Worker.

This allows more fine-grained control over transformation request flows and cache behavior. For example, you can resize, manipulate, and overlay images without requiring them to be accessible through a URL.

The Images binding can be configured in the Cloudflare dashboard for your Worker or in the Wrangler configuration file in your project's directory:

{
	"images": {
		"binding": "IMAGES", // i.e. available in your Worker on env.IMAGES
	},
}
[images]
binding = "IMAGES"

Within your Worker code, you can interact with this binding by using env.IMAGES.

Here's how you can rotate, resize, and blur an image, then output the image as AVIF:

const info = await env.IMAGES.info(stream);
// stream contains a valid image, and width/height is available on the info object

const response = (
	await env.IMAGES.input(stream)
		.transform({ rotate: 90 })
		.transform({ width: 128 })
		.transform({ blur: 20 })
		.output({ format: "image/avif" })
).response();

return response;

For more information, refer to Images Bindings.

Rewind, Replay, Resume: Introducing DVR for Stream Live

Previously, all viewers watched "the live edge," or the latest content of the broadcast, synchronously. If a viewer paused for more than a few seconds, the player would automatically "catch up" when playback started again. Seeking through the broadcast was only available once the recording was available after it concluded.

Starting today, customers can make a small adjustment to the player embed or manifest URL to enable the DVR experience for their viewers. By offering this feature as an opt-in adjustment, our customers are empowered to pick the best experiences for their applications.

When building a player embed code or manifest URL, just add dvrEnabled=true as a query parameter. There are some things to be aware of when using this option. For more information, refer to DVR for Live.