Open source. Your Cloudflare account.
Your images.
Optimized at the edge.
Resize to WebP. Cache globally. Keep control.
A small, self-hosted image optimizer for Cloudflare and Next.js.
Sharp Workers R2
01 / PLAYGROUND
See the difference.
Pick a width and quality. Compare file sizes.
Run it again to see the cache at work.
- Original JPEG
- Measuring…
- Optimized WebP
- Not yet
- Size change
- Not yet
Start at 1200 px and quality 85, or choose your own settings.
02 / INTEGRATION
Use with Next.js.
Your usual next/image component.
Your own image infrastructure.
Next.js handles responsive sizes and lazy loading. imgt serves the WebP. Add these three files to your app, then restart its development server.
Set it up with your agent.
Copy a prompt to deploy imgt in your own Cloudflare account and connect it to your project.
View the setup prompt
Help me self-host imgt on my Cloudflare account and integrate it into my project.
Read https://imgt.fashn.ai/llms.txt and follow its deployment, configuration and
Next.js guides before making changes. The source is
https://github.com/fashn-AI/imgt. Inspect my project and preserve its existing
configuration. Ask for missing information: my target Cloudflare
account, my own imgt deployment URL and the public HTTPS image sources I use.
1. Set up my own imgt deployment through Cloudflare's GitHub integration.
Production branch pushes should deploy automatically. Use Wrangler to
inspect the intended account; do not manually deploy or assume the account
open in a browser is the correct one.
2. Set ALLOWED_HOSTS to the hosts serving my original images. Include
images.unsplash.com if I want the playground. Wildcards such as *.example.com
do not include the parent example.com. Validate redirect hosts too. Follow
the deployment guide's GitHub rebuild step after dashboard variable updates.
3. Keep the imgt R2 bucket private, bound as DERIVATIVES, using Standard storage.
Complete storage setup with an enabled lifecycle rule that deletes objects
under derivatives/ after 30 days. Deployment does not add this rule.
4. If my app uses Next.js, adapt the TypeScript custom image loader to my
existing configuration. Use my own imgt URL, configure the qualities my
components use and preserve image dimensions and responsive sizes.
Otherwise, use the /image API from my project with my own endpoint.
5. Replace every endpoint placeholder with my deployment URL. The FASHN AI
website is a demonstration, not a shared optimization service for my app.
6. Run the repository checks and test the deployed image pipeline using
npm run smoke -- MY_IMGT_ENDPOINT MY_PUBLIC_IMAGE_URL. Verify decoded WebP
output, repeat cache hits and conditional 304 responses. Do not treat
/health alone as an end-to-end check. Only use --fresh with source URLs that
accept additional query parameters.
Keep the implementation small, explain the changes and call out any setup
steps I still need to complete in Cloudflare.
-
Configure the loader.
List the quality values your Next.js components use.
next.config.tsimport type { NextConfig } from "next"; const nextConfig: NextConfig = { images: { loader: "custom", loaderFile: "./image-loader.ts", qualities: [77], }, }; export default nextConfig; -
Point it at imgt.
Replace the placeholder with your own deployed imgt URL. Deploy your own instance before integrating; this website provides a playground, not a shared optimization service.
image-loader.ts"use client"; import type { ImageLoaderProps } from "next/image"; export default function imageLoader({ src, width, quality = 77, }: ImageLoaderProps) { const url = new URL("/image", "https://imgt.YOUR-SUBDOMAIN.workers.dev"); url.search = new URLSearchParams({ url: src, w: String(width), q: String(quality), }).toString(); return url.href; } -
Use your Image.
Set dimensions and sizes to match your layout. imgt accepts integer widths from 1 to 3840 and qualities from 1 to 100; this example uses 77.
app/page.tsximport Image from "next/image"; export default function Page() { return ( <main style={{ maxWidth: 800, margin: "0 auto" }}> <Image src="https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?fm=jpg&fit=crop&w=1920&h=1200&q=90" alt="Landscape from Unsplash" width={1920} height={1200} quality={77} sizes="(max-width: 768px) 100vw, 800px" style={{ width: "100%", height: "auto" }} /> </main> ); }
03 / CONFIGURATION
Allow your image domains.
Choose where originals come from.
Set the list in Cloudflare.
The ALLOWED_HOSTS environment variable lists the hosts
imgt can fetch original images from. For
https://cdn.example.com/photo.jpg, allow
cdn.example.com. Include your frontend domain only if it
serves the originals. This setting does not restrict which websites
can display optimized images.
Set it in the dashboard.
In Cloudflare, open Workers & Pages, select your Worker, then
go to Settings → Variables and Secrets. Add or edit a Text
variable named ALLOWED_HOSTS.
Paste your comma-separated hosts and select Deploy. Keep
images.unsplash.com for the playground. This value is
preserved when you deploy code updates.
After saving, run your production GitHub build before testing images. See the deployment guide for details.
images.unsplash.com,cdn.example.com,*.images.example.com
| Entry | Allows |
|---|---|
cdn.example.com |
That exact host. Subdomains need their own entry. |
*.images.example.com |
Any subdomain, including nested ones. The parent
images.example.com needs a separate entry.
|
04 / STORAGE
Set up cache cleanup.
Keep cached images for 30 days.
Regenerate them when needed.
Complete this step before production use. Deployment creates the
private imgt R2 bucket, but does not add a lifecycle
rule. Without one, unused cached images remain in storage and
accumulate.
Add a lifecycle rule.
In Cloudflare, open R2 → imgt → Settings → Object Lifecycle Rules → Add rule. Enter these settings, enable the rule and save it. Use your own bucket name if you changed the default.
| Setting | Value |
|---|---|
| Rule name | expire-cached-images |
| Prefix | derivatives/ |
| Action | Delete objects after 30 days |
| Status | Enabled |