A minimal Cloudflare Worker that converts images to WebP. This repository contains a Worker script that accepts an image via POST, resizes it, converts it to WebP and returns the converted bytes.
This README describes exactly what the current worker does (no marketing fluff) and how to deploy and use it.
- Accepts only HTTP POST requests. Any non-POST request returns 405 with body "Send POST".
- Reads the entire request body as raw bytes (expects an image in the request body or a multipart/form-data upload where the file bytes are in the body you send).
- Uses @cf-wasm/photon to:
- Create a PhotonImage from the uploaded bytes.
- Resize the image to a fixed max width of 1200 pixels.
- Calculate height using a hard-coded aspect ratio: (778 / 1376) * 1200 (this matches an original image size of 1376×778 present in the code).
- Use SamplingFilter.Lanczos3 for the resize.
- Encode the resized image to WebP at quality 60 (fixed).
- Frees PhotonImage resources after conversion.
- Returns the WebP bytes with headers:
Content-Type: image/webpContent-Disposition: attachment; filename="converted.webp"
- On error, returns HTTP 500 with a body prefixed by "Error: " and the thrown error message.
- Resize height is computed using a hard-coded aspect ratio. If your input image has a different aspect ratio than 1376×778, the height calculation will not preserve the original aspect ratio and the output may be distorted.
- Quality is fixed at 60 in the current code.
- There are no query parameters, headers, or payload keys implemented to change quality, lossless mode, output name, or size — those would need to be added in code.
- The Worker reads the entire request body into memory; very large uploads may hit Cloudflare Worker CPU/ memory limits.
- Multipart/form-data parsing is not explicitly implemented in the sample code — sending raw image bytes in the POST body is the simplest, supported approach. If you post a multipart form you must make sure the file bytes are what reach request.arrayBuffer() (typical multipart requires parsing).
- No authentication, rate limiting, logging, or storage/caching is included.
- CORS headers are not added by the shown code — add them if you will be calling the Worker from browsers.
- Clone the repo:
git clone https://github.com/SiaLabs/my-webp-converter.git
cd my-webp-converter- Install dependencies (if you will build or manage packages locally):
npm install
# or
pnpm install
# or
yarn- Install Wrangler (Cloudflare CLI) if you haven't and want to use it locally:
npm install -g wrangler- Add a
wrangler.toml(example):
name = "my-webp-converter"
compatibility_date = "2026-01-25"
account_id = "your-cloudflare-account-id"
# optional: route = "example.com/convert"
# optional: zone_id = "your-zone-id"- Authenticate and publish:
wrangler login
wrangler publishAfter publish, Wrangler prints the worker URL, e.g. https://<name>.<your-subdomain>.workers.dev.
For local testing:
wrangler devIf you prefer not to install Wrangler locally, use GitHub Actions to publish from this repository to your Cloudflare account. This requires a Cloudflare API token and your Account ID.
-
Create a Cloudflare API token with permissions to publish Workers. Recommended permissions:
- Account: Workers Scripts: Edit
- (optional) Account: Workers KV: if using KV bindings Save the token somewhere secure.
-
In the GitHub repository where you want to deploy (your fork or this repository), add the following repository Secrets:
CF_ACCOUNT_ID— your Cloudflare Account IDCF_API_TOKEN— the API token you created
-
Add the GitHub Actions workflow included in this repo (this project includes
.github/workflows/deploy.yml) — when you push tomainthe workflow will run and publish the worker.
Notes:
- The workflow runs
wrangler publishin CI (no Wrangler install on your local machine required). - Ensure
wrangler.tomlis present and correct in the repo (account_id especially). - The workflow will publish on every push to
main(modify triggers to fit your workflow).
- Send a raw image file in the POST body (recommended):
curl -X POST "https://<your-worker>.workers.dev/convert" \
-H "Content-Type: image/jpeg" \
--data-binary '@input.jpg' \
-o output.webpThis writes the returned WebP bytes to output.webp. The worker returns Content-Disposition: attachment, so curl will still receive raw bytes — use -o to save.
- Send image bytes from stdin:
cat input.png | curl -X POST "https://<your-worker>.workers.dev/convert" \
-H "Content-Type: image/png" \
--data-binary @- -o output.webp- Non-POST test:
curl -I "https://<your-worker>.workers.dev/"
# returns 405 and body "Send POST"- If output is distorted: that is expected if the input image aspect ratio is not ~1376×778 (the current code uses that ratio). To preserve aspect ratio for arbitrary inputs, update the code to compute the height from the input image's width/height instead of the hard-coded numbers.
- If request fails for large files: consider resizing client-side before upload or implement streaming/chunked processing if feasible. Also monitor usage against Cloudflare Worker limits.
- If you want to call from a browser and see CORS errors: add appropriate
Access-Control-Allow-Originheaders in the worker response.
The current file is explicit and small. For broader compatibility, consider:
- Computing output height from the actual PhotonImage dimensions instead of a hard-coded ratio.
- Allowing
qualityandmaxWidthto be set via query params or JSON in the POST body. - Adding basic size checks and returning 400 for unsupported content-type or empty body.
- Adding CORS headers when needed for browser clients.
- Adding limits (max bytes) to avoid running into Worker execution limits.
This repo is intentionally minimal. If you send a PR, please:
- Keep changes small and well-documented.
- If adding new options, include argument validation and tests where reasonable.
MIT