Image optimization
Resize and re-encode images at the edge under a policy that lists the sizes, qualities, formats and sources you allow.
Gigadrive Network can resize and re-encode an image at request time, from a local deployment asset or an approved remote URL. The work happens at the edge, so an optimized image never invokes a Function.
The URL
Every optimized image is addressed under one canonical prefix:
/_gigadrive/image/<url-encoded source>/<filename>?width=1200&quality=75&format=webp&fit=coverThe source is either a path inside your deployment, such as /assets/hero.jpg, or an absolute http or https URL. The trailing filename segment is cosmetic and exists so downloads and logs stay readable.
Two other shapes redirect to that URL rather than being served directly. /_next/image?url=…&w=…&q=… answers with a permanent 308, so a Next.js application keeps working without touching its image code. So does ?variant=thumb, which expands to the parameters that variant names.
There is one temporary redirect. When you do not pass format= and the source is not an SVG, the edge reads the Accept header and sends a 307 to an AVIF or WebP URL if the browser accepts one and the policy allows it. That response carries vary: Accept, so the negotiation does not poison the cache for a browser that asked for something else.
Only GET and HEAD reach the optimizer. Anything else returns 405 with Allow: GET, HEAD.
Configuring the policy
The policy is fixed at build time and immutable for the life of a deployment. Where it comes from depends on the project:
- Next.js. The policy is derived from the
imagesblock innext.config.deviceSizesandimageSizesbecome the allowed widths, andqualities,formats,remotePatterns,localPatterns,minimumCacheTTL,dangerouslyAllowSVG,contentSecurityPolicyandcontentDispositionTypecarry over unchanged.next.confighas nothing to express named variants with, so a Next.js deployment gets none and?variant=returns 400. Settingimages.unoptimized: trueturns the whole feature off for the deployment. - Projects with no detected framework. Add an
imagesblock togigadrive.yaml. Supplying the block at all materializes the full policy, with every key you leave out filled in from the following defaults.
version: 4
assets: public
images:
remotePatterns:
- protocol: https
hostname: cdn.example.com
pathname: /images/**
widths: [640, 1080, 1920]
heights: [320, 640]
qualities: [60, 80]
formats: [image/avif, image/webp]
minimumCacheTTL: 86400
variants:
thumb:
width: 320
height: 320
fit: cover
format: image/webp| Key | Default | Notes |
|---|---|---|
widths | [640, 750, 828, 1080, 1200, 1920, 2048, 3840] | 1 to 64 entries, each 1 to 8192 |
heights | [] | Empty means height= is rejected |
qualities | [75] | Each 1 to 100 |
formats | ['image/avif', 'image/webp'] | Also accepts image/jpeg and image/png |
localPatterns | [{ pathname: '/**' }] | Which deployment paths may be optimized |
remotePatterns | [] | hostname is required on each entry |
minimumCacheTTL | 14400 | Seconds, 0 to 31536000 |
dangerouslyAllowSVG | false | SVG behavior differs from Next.js |
contentSecurityPolicy | default-src 'self'; script-src 'none'; sandbox; | Sent on every image response |
contentDispositionType | attachment | Or inline |
maximumRedirects | 3 | Followed when fetching a remote source |
maximumResponseBody | 52428800 | 50 MiB, enforced while streaming |
variants | {} | Up to 100 named presets |
The policy is an allowlist, not a set of hints. A width that is not in widths returns 400, and the same goes for quality and format. fit is the exception, checked against a fixed set rather than your policy: contain, cover, fill, inside and outside. Because heights defaults to empty and Next.js projects always receive an empty list, height= fails until you list the heights you want on a project configured through gigadrive.yaml.
Remote sources
A remote source has to match one of your remotePatterns before the edge fetches anything. After that, the hostname is resolved and the address it resolves to is checked: private, loopback, link-local and multicast ranges are refused. A remote fetch times out after 10 seconds.
SVG
An SVG is never transformed. What differs is what happens instead, and it depends on where the file came from.
A local deployment asset that is an SVG is passed through as-is, marked with x-gigadrive-image-transform: passthrough. Denying it would break rendering without protecting anything, since the same bytes are already reachable at the asset's own path. A remote SVG is refused with 415 and the code IMAGE_SVG_DENIED unless you set dangerouslyAllowSVG, because reflecting third-party markup onto your own origin is the case that flag exists for.
On a Next.js project the injected image loader returns .svg sources unchanged, so they never make the round trip at all.
Caching and purging
A successful image response carries cache-control: public, max-age=<minimumCacheTTL>, stale-while-revalidate=86400, your configured content security policy, x-content-type-options: nosniff, and x-gigadrive-image-source: local or remote.
Each response is tagged twice: once for the deployment and once for the source, so you can invalidate every transform of one image without touching the rest. Purging needs the network:image_optimization:purge scope, and inspecting the policy needs network:image_optimization:read.
import { GigadriveClient, createManagedImageUrl } from '@gigadrive/sdk';
// Build the URL. This is a pure function and makes no API call.
const url = createManagedImageUrl({
origin: 'https://app.gigadrive.app',
source: '/assets/hero.jpg',
width: 1200,
quality: 75,
format: 'webp',
fit: 'cover',
});
console.log(url);
// https://app.gigadrive.app/_gigadrive/image/%2Fassets%2Fhero.jpg/hero.jpg?width=1200&quality=75&format=webp&fit=cover
// Then invalidate every transform of that source after replacing the file.
// The client reads GIGADRIVE_CLIENT_ID and GIGADRIVE_CLIENT_SECRET from the environment.
const client = new GigadriveClient();
const deploymentId = '0197b2f2-3c5e-7d1f-9b3a-333333333333';
const status = await client.imageOptimization.inspect(deploymentId, '/assets/hero.jpg');
console.log(status.enabled, status.sourceTag);
const result = await client.imageOptimization.purge(deploymentId, '/assets/hero.jpg');
console.log(result.tag);Omit source and the call targets the deployment-wide tag, which purges every optimized image of that deployment. A purge returns 409 when image optimization is not enabled for the deployment, and 503 when the CDN does not confirm it.
Error codes
Every failure is text/plain with an x-gigadrive-image-error header naming the code, and cache-control: no-store.
| Code | Status | Cause |
|---|---|---|
IMAGE_SOURCE_MISSING | 400 | /_next/image called without url |
IMAGE_SOURCE_INVALID | 400 | Undecodable source, or a remote URL carrying credentials |
IMAGE_SOURCE_DENIED | 400 | No remotePatterns or localPatterns entry matched |
IMAGE_SOURCE_PRIVATE_ADDRESS | 400 | The hostname resolved to a private or link-local address |
IMAGE_WIDTH_INVALID | 400 | width is not an integer listed in widths |
IMAGE_HEIGHT_INVALID | 400 | height is not an integer listed in heights |
IMAGE_QUALITY_INVALID | 400 | quality is not an integer listed in qualities |
IMAGE_FORMAT_INVALID | 400 | format is not listed in formats |
IMAGE_FIT_INVALID | 400 | fit is not one of the five accepted values |
IMAGE_VARIANT_INVALID | 400 | variant names a preset the policy does not define |
IMAGE_SOURCE_NOT_FOUND | 404 | The local source is not part of this deployment |
IMAGE_SOURCE_TOO_LARGE | 413 | The source exceeds maximumResponseBody |
IMAGE_CONTENT_TYPE_INVALID | 415 | The source is not an image/* content type |
IMAGE_SVG_DENIED | 415 | A remote SVG while dangerouslyAllowSVG is false |
IMAGE_SOURCE_DNS_FAILED | 502 | The hostname did not resolve |
IMAGE_SOURCE_BAD_RESPONSE | 404 if upstream 404, else 502 | The upstream returned a non-2xx status |
IMAGE_SOURCE_REDIRECT_LIMIT | 502 | More redirects than maximumRedirects |
IMAGE_SOURCE_FETCH_FAILED | 502 | Transport failure or fetch timeout |
IMAGE_SOURCE_READ_FAILED | 502 | The source body could not be read |
Successful optimizations are counted as the image_optimizations metric, broken down per application in Usage. Redirects and failures are not counted.
