Zum Inhalt springen
GigadriveDocs

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=cover

The 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 images block in next.config. deviceSizes and imageSizes become the allowed widths, and qualities, formats, remotePatterns, localPatterns, minimumCacheTTL, dangerouslyAllowSVG, contentSecurityPolicy and contentDispositionType carry over unchanged. next.config has nothing to express named variants with, so a Next.js deployment gets none and ?variant= returns 400. Setting images.unoptimized: true turns the whole feature off for the deployment.
  • Projects with no detected framework. Add an images block to gigadrive.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
KeyDefaultNotes
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
minimumCacheTTL14400Seconds, 0 to 31536000
dangerouslyAllowSVGfalseSVG behavior differs from Next.js
contentSecurityPolicydefault-src 'self'; script-src 'none'; sandbox;Sent on every image response
contentDispositionTypeattachmentOr inline
maximumRedirects3Followed when fetching a remote source
maximumResponseBody5242880050 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.

CodeStatusCause
IMAGE_SOURCE_MISSING400/_next/image called without url
IMAGE_SOURCE_INVALID400Undecodable source, or a remote URL carrying credentials
IMAGE_SOURCE_DENIED400No remotePatterns or localPatterns entry matched
IMAGE_SOURCE_PRIVATE_ADDRESS400The hostname resolved to a private or link-local address
IMAGE_WIDTH_INVALID400width is not an integer listed in widths
IMAGE_HEIGHT_INVALID400height is not an integer listed in heights
IMAGE_QUALITY_INVALID400quality is not an integer listed in qualities
IMAGE_FORMAT_INVALID400format is not listed in formats
IMAGE_FIT_INVALID400fit is not one of the five accepted values
IMAGE_VARIANT_INVALID400variant names a preset the policy does not define
IMAGE_SOURCE_NOT_FOUND404The local source is not part of this deployment
IMAGE_SOURCE_TOO_LARGE413The source exceeds maximumResponseBody
IMAGE_CONTENT_TYPE_INVALID415The source is not an image/* content type
IMAGE_SVG_DENIED415A remote SVG while dangerouslyAllowSVG is false
IMAGE_SOURCE_DNS_FAILED502The hostname did not resolve
IMAGE_SOURCE_BAD_RESPONSE404 if upstream 404, else 502The upstream returned a non-2xx status
IMAGE_SOURCE_REDIRECT_LIMIT502More redirects than maximumRedirects
IMAGE_SOURCE_FETCH_FAILED502Transport failure or fetch timeout
IMAGE_SOURCE_READ_FAILED502The 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.