コンテンツにスキップ

Workersを使用した変換

Cloudflare Workersを使用してカスタムURLスキームで変換することで、すべての画像リクエストに対して強力なプログラム制御を提供します。

以下は、Workersが提供する柔軟性のいくつかの例です:

  • カスタムURLスキームを使用。画像URLにピクセル寸法を指定する代わりに、thumbnaillargeなどのプリセット名を使用します。
  • 元の画像の実際の場所を隠す。画像を外部のS3バケットやサーバー上の隠しフォルダーに保存し、その情報をURLに露出させることなく管理できます。
  • コンテンツネゴシエーションを実装。これは、デバイスやネットワークの状態に基づいて画像のサイズ、形式、品質を動的に適応させるのに役立ちます。

リサイズ機能は、fetch()サブリクエスト内のオプションを介してアクセスされます。

Fetchオプション

fetch()関数は、第二引数の{cf: {image: {…}}}オブジェクト内でパラメータを受け入れます。

anim

Whether to preserve animation frames from input files. Default is true. Setting it to false reduces animations to still images. This setting is recommended when enlarging images or processing arbitrary user content, because large GIF animations can weigh tens or even hundreds of megabytes. It is also useful to set anim:false when using format:"json" to get the response quicker without the number of frames.

anim=false

background

Background color to add underneath the image. Applies to images with transparency (for example, PNG) and images resized with fit=pad. Accepts any CSS color using CSS4 modern syntax, such as rgb(255 255 0) and rgba(255 255 0 100).

background=%23RRGGBB
OR
background=red

blur

Blur radius between 1 (slight blur) and 250 (maximum). Be aware that you cannot use this option to reliably obscure image content, because savvy users can modify an image’s URL and remove the blur option. Use Workers to control which options can be set.

blur=50

border

Adds a border around the image. The border is added after resizing. Border width takes dpr into account, and can be specified either using a single width property, or individually for each side.

cf: {image: {border: {color: "rgb(0,0,0,0)", top: 5, right: 10, bottom: 5, left: 10}}}
cf: {image: {border: {color: "#FFFFFF", width: 10}}}

brightness

Increase brightness by a factor. A value of 1.0 equals no change, a value of 0.5 equals half brightness, and a value of 2.0 equals twice as bright. 0 is ignored.

brightness=0.5

compression

Slightly reduces latency on a cache miss by selecting a quickest-to-compress file format, at a cost of increased file size and lower image quality. It will usually override the format option and choose JPEG over WebP or AVIF. We do not recommend using this option, except in unusual circumstances like resizing uncacheable dynamically-generated images.

compression=fast

contrast

Increase contrast by a factor. A value of 1.0 equals no change, a value of 0.5 equals low contrast, and a value of 2.0 equals high contrast. 0 is ignored.

contrast=0.5

dpr

Device Pixel Ratio. Default is 1. Multiplier for width/height that makes it easier to specify higher-DPI sizes in <img srcset>.

dpr=1

fit

Affects interpretation of width and height. All resizing modes preserve aspect ratio. Used as a string in Workers integration. Available modes are:

  • scale down
    Similar to contain, but the image is never enlarged. If the image is larger than given width or height, it will be resized. Otherwise its original size will be kept. Example:
fit=scale-down
  • contain
    Image will be resized (shrunk or enlarged) to be as large as possible within the given width or height while preserving the aspect ratio. If you only provide a single dimension (for example, only width), the image will be shrunk or enlarged to exactly match that dimension.
fit=contain
  • cover
    Resizes (shrinks or enlarges) to fill the entire area of width and height. If the image has an aspect ratio different from the ratio of width and height, it will be cropped to fit.
fit=cover
  • crop
    Image will be shrunk and cropped to fit within the area specified by width and height. The image will not be enlarged. For images smaller than the given dimensions, it is the same as scale-down. For images larger than the given dimensions, it is the same as cover. See also trim
fit=crop
  • pad
    Resizes to the maximum size that fits within the given width and height, and then fills the remaining area with a background color (white by default). This mode is not recommended, since you can achieve the same effect more efficiently with the contain mode and the CSS object-fit: contain property.
fit=pad

format

The auto option will serve the WebP or AVIF format to browsers that support it. If this option is not specified, a standard format like JPEG or PNG will be used. Cloudflare will default to JPEG when possible due to the large size of PNG files.

Workers integration supports:

  • avif: Generate images in AVIF format if possible (with WebP as a fallback).
  • webp: Generate images in Google WebP format. Set the quality to 100 to get the WebP lossless format.
  • jpeg: Generate images in interlaced progressive JPEG format, in which data is compressed in multiple passes of progressively higher detail.
  • baseline-jpeg: Generate images in baseline sequential JPEG format. It should be used in cases when target devices don’t support progressive JPEG or other modern file formats.
  • json: Instead of generating an image, outputs information about the image in JSON format. The JSON object will contain data such as image size (before and after resizing), source image’s MIME type, and file size.
format=auto

For the format:auto option to work with a custom Worker, you need to parse the Accept header. Refer to this example Worker for a complete overview of how to set up an image transformation Worker.

Custom Worker for Image Resizing with format:auto
const accept = request.headers.get("accept");
let image = {};
if (/image\/avif/.test(accept)) {
image.format = "avif";
} else if (/image\/webp/.test(accept)) {
image.format = "webp";
}
return fetch(url, {cf:{image}});

gamma

Increase exposure by a factor. A value of 1.0 equals no change, a value of 0.5 darkens the image, and a value of 2.0 lightens the image. 0 is ignored.

gamma=0.5

gravity

When cropping with fit: "cover" and fit: "crop", this parameter defines the side or point that should not be cropped. Available options are:

  • auto
    Selects focal point based on saliency detection (using maximum symmetric surround algorithm).
gravity=auto
  • side
    A side ("left", "right", "top", "bottom") or coordinates specified on a scale from 0.0 (top or left) to 1.0 (bottom or right), 0.5 being the center. The X and Y coordinates are separated by lowercase x in the URL format. For example, 0x1 means left and bottom, 0.5x0.5 is the center, 0.5x0.33 is a point in the top third of the image.

    For the Workers integration, use an object {x, y} to specify coordinates. It contains focal point coordinates in the original image expressed as fractions ranging from 0.0 (top or left) to 1.0 (bottom or right), with 0.5 being the center. {fit: "cover", gravity: {x:0.5, y:0.2}} will crop each side to preserve as much as possible around a point at 20% of the height of the source image.

gravity=left
or
gravity=0x1

height

Specifies maximum height of the image in pixels. Exact behavior depends on the fit mode (described below).

height=250

metadata

Controls amount of invisible metadata (EXIF data) that should be preserved. Color profiles and EXIF rotation are applied to the image even if the metadata is discarded. Note that if the Polish feature is enabled, all metadata may have been removed already and this option will have no effect.

Options include:

  • keep
    Preserves most of EXIF metadata, including GPS location if present.
metadata=keep
  • copyright
    Discard all metadata except EXIF copyright tag. This is the default behavior for JPEG images.
metadata=copyright
  • none
    Discard all invisible EXIF metadata. Currently, WebP and PNG output formats always discard metadata.
metadata=none

onerror

In case of a fatal error that prevents the image from being resized, redirects to the unresized source image URL. This may be useful in case some images require user authentication and cannot be fetched anonymously via Worker. This option should not be used if there is a chance the source image is very large. This option is ignored if the image is from another domain, but you can use it with subdomains.

onerror=redirect

quality

Specifies quality for images in JPEG, WebP, and AVIF formats. The quality is in a 1-100 scale, but useful values are between 50 (low quality, small file size) and 90 (high quality, large file size). 85 is the default. When using the PNG format, an explicit quality setting allows use of PNG8 (palette) variant of the format.

quality=50

rotate

Number of degrees (90, 180, or 270) to rotate the image by. width and height options refer to axes after rotation.

rotate=90

sharpen

Specifies strength of sharpening filter to apply to the image. The value is a floating-point number between 0 (no sharpening, default) and 10 (maximum). 1 is a recommended value for downscaled images.

sharpen=2

trim

Specifies a number of pixels to cut off on each side. Allows removal of borders or cutting out a specific fragment of an image. Trimming is performed before resizing or rotation. Takes dpr into account. For image transformations and Cloudflare Images, use as four numbers in pixels separated by a semicolon, in the form of top;right;bottom;left or via separate values trim.width,trim.height, trim.left,trim.top. For the Workers integration, specify an object with properties: {top, right, bottom, left, width, height}.

trim=20;30;20;0
trim.width=678
trim.height=678
trim.left=30
trim.top=40

width

Specifies maximum width of the image in pixels. Exact behavior depends on the fit mode (described below).

width=250

あなたのWorker内で、fetch(request)を使用して画像を取得する場所に、以下の例のようにオプションを追加します:

fetch(imageURL, {
cf: {
image: {
fit: "scale-down",
width: 800,
height: 600
}
}
})

これらの型定義は、私たちのWorkers TypeScript定義ライブラリでも利用可能です。

Workerの設定

CloudflareダッシュボードのWorkersセクションで新しいスクリプトを作成します。Workerスクリプトを、/images/*/assets/*などのアセット提供専用のパスにスコープします。リサイズできるのはサポートされている画像形式のみです。他のタイプのリソース(CSS、HTML)をリサイズしようとするとエラーが発生します。

Workerが処理するパスは、元の(リサイズされていない)画像へのパスとは別に保つことが最善です。これは、画像リサイズWorkerが自分自身を呼び出すことによってリクエストループを引き起こすのを避けるためです。例えば、画像をexample.com/originals/ディレクトリに保存し、/originals/ディレクトリから画像を取得するexample.com/thumbnails/*パスを介してリサイズを処理します。ソース画像がWorkerによって処理される場所に保存されている場合、Workerが無限ループを作成しないようにする必要があります。

リクエストループを防ぐ

リサイズと最適化を行うために、Workerは元のリサイズされていない画像をオリジンサーバーから取得できる必要があります。Workerが処理するパスがサーバー上の画像が保存されているパスと重なると、Workerが自分自身から画像をリクエストしようとして無限ループを引き起こす可能性があります。

どのリクエストがオリジンサーバーに直接行くべきかを検出する必要があります。Viaヘッダーにimage-resizingという文字列が含まれている場合、それは別のWorkerからのリクエストであり、オリジンサーバーに向けるべきです:

addEventListener("fetch", event => {
// このリクエストが画像リサイズWorkerから来ている場合、
// 再度リサイズして無限ループを引き起こさないようにします:
if (/image-resizing/.test(event.request.headers.get("via"))) {
return fetch(event.request)
}
// ここで安全に画像リサイズを使用できます
}

ダッシュボードでのプレビューの欠如

Workerエディタのスクリプトプレビューはfetch()オプションを無視し、常にリサイズされていない画像を取得します。画像変換の効果を確認するには、Workerスクリプトをデプロイし、エディタの外で使用する必要があります。

エラーハンドリング

画像がリサイズできない場合(例えば、画像が存在しないか、リサイズパラメータが無効な場合)、レスポンスはエラーを示すHTTPステータスを持ちます(例えば、400404、または502)。

デフォルトでは、エラーはブラウザに転送されますが、エラーの処理方法を決定できます。例えば、ブラウザを元のリサイズされていない画像にリダイレクトすることもできます:

const response = await fetch(imageURL, options)
if (response.ok || response.redirected) { // fetch()は304ステータスで応答することがあります
return response
} else {
return response.redirect(imageURL, 307)
}

サーバー上の元の画像が非常に大きい場合、失敗した画像を表示しない方が良いかもしれません。過度に大きな画像にフォールバックすると、帯域幅やメモリを過剰に使用したり、ページレイアウトが崩れたりする可能性があります。

失敗した画像をプレースホルダー画像に置き換えることもできます:

const response = await fetch(imageURL, options)
if (response.ok || response.redirected) {
return response
} else {
// サーバー上のURLに変更
return fetch("https://img.example.com/blank-placeholder.png")
}

例としてのWorker

https://example.com/image-resizingでWorkerをセットアップしたと仮定し、https://example.com/image-resizing?width=80&image=https://example.com/uploads/avatar1.jpgのようなURLを処理します:

addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request))
})
/**
* リクエストを取得してログに記録
* @param {Request} request
*/
async function handleRequest(request) {
// リクエストURLを解析してクエリ文字列にアクセス
let url = new URL(request.url)
// Cloudflare特有のオプションはcfオブジェクトにあります。
let options = { cf: { image: {} } }
// クエリ文字列からリクエストオプションにパラメータをコピーします。
// ここでさまざまなパラメータを実装できます。
if (url.searchParams.has("fit")) options.cf.image.fit = url.searchParams.get("fit")
if (url.searchParams.has("width")) options.cf.image.width = url.searchParams.get("width")
if (url.searchParams.has("height")) options.cf.image.height = url.searchParams.get("height")
if (url.searchParams.has("quality")) options.cf.image.quality = url.searchParams.get("quality")
// あなたのWorkerは自動フォーマットネゴシエーションを担当します。Acceptヘッダーを確認します。
const accept = request.headers.get("Accept");
if (/image\/avif/.test(accept)) {
options.cf.image.format = 'avif';
} else if (/image\/webp/.test(accept)) {
options.cf.image.format = 'webp';
}
// リサイズする元の(フルサイズ)画像のURLを取得します。
// ここでURLを調整することができます。例えば、サーバーの固定アドレスでプレフィックスを付けて、
// ユーザーが目にするURLを短くてクリーンにします。
const imageURL = url.searchParams.get("image")
if (!imageURL) return new Response('Missing "image" value', { status: 400 })
try {
// TODO: バリデーションロジックをカスタマイズ
const { hostname, pathname } = new URL(imageURL)
// オプションで、JPEG、PNG、GIF、またはWebPファイル拡張子を持つURLのみを許可
// @see https://developers.cloudflare.com/images/url-format#supported-formats-and-limitations
if (!/\.(jpe?g|png|gif|webp)$/i.test(pathname)) {
return new Response('Disallowed file extension', { status: 400 })
}
// デモ: "example.com"の画像のみを受け入れる
if (hostname !== 'example.com') {
return new Response('Must use "example.com" source images', { status: 403 })
}
} catch (err) {
return new Response('Invalid "image" value', { status: 400 })
}
// リクエストヘッダーを通過させるリクエストを構築
const imageRequest = new Request(imageURL, {
headers: request.headers
})
// リサイズオプションを持つfetch()を返すことで、リサイズされた画像を持つレスポンスを通過させます。
return fetch(imageRequest, options)
}

画像リサイズをテストする際は、まずスクリプトをデプロイしてください。ダッシュボードのオンラインエディタではリサイズはアクティブになりません。

cacheKeyに関する警告

リサイズされた画像は常にキャッシュされます。これらは、フルサイズのソース画像のURLに対するキャッシュエントリの下に追加のバリアントとしてキャッシュされます。多くの異なるWorkersや多くの外部URLを使用しても心配しないでください — それらはリサイズされた画像のキャッシュに影響を与えず、リサイズされた画像が正しくキャッシュされるために何もする必要はありません。

複数の異なるソースURLのキャッシュを統一するためにcacheKeyfetchオプションを使用する場合、cacheKeyにリサイズオプションを追加してはいけません。これによりキャッシュが断片化され、キャッシュパフォーマンスが低下します。cacheKeyオプションは、フルサイズのソース画像URLのみに適用され、リサイズされたバリアントには適用されません。