Plain <img>
src and alt interpolate like any
other attribute, and the tag comes out as written:
The <Image> component
<Image> is built into the compiler. It renders a <picture> offering AVIF and
WebP with a JPEG fallback, all three pointing at an image service:
<Image> is a URL generator. It does no work at build time and touches no
pixels. Those URLs 404 until something is serving /_slurp/image.
Props
The URL is fixed and root-relative
/_slurp/image is hardcoded. There is no base-path option and no way for a
theme to point at another host, so the service must answer on the same origin
as the page, behind a reverse proxy.
Running the service
GET /_slurp/image. Proxy /_slurp/image from the web server to
port 3001 so the page and the images share an origin.
The defaults are closed. This service fetches URLs an attacker may have chosen,
so it binds 127.0.0.1 and sends no CORS headers. Widen only as needed.
Flags
RUST_LOG is the only environment variable it reads.
Requesting a transform
There is no
h and no q, and the format parameter is f, not fmt. Height
is not a transform input at all: the <Image> component uses it for the <img>
attribute so the browser can reserve space.
The only transform is a width-only Lanczos3 resize. It preserves aspect
ratio and never upscales, so asking for a width larger than the source returns
the source size. Quality is fixed.
Formats in and out
A PNG or GIF source is transcoded to one of the three output formats, never
re-emitted as itself. An AVIF or HEIC source is recognised only so it can be
reported as unsupported rather than as corrupt data.
Errors
Every error is JSON, shaped{"error": "..."}.
A successful response carries the output MIME type and
cache-control: public, max-age=<cache-ttl>.
Request guards
The service takes a URL from a query string and fetches it, so it is a server-side request forgery primitive unless written not to be. The guards:- Scheme allowlist. Only
httpandhttps. Anything else isinvalid source URL, including a URL carrying userinfo. - DNS resolved up front, every returned address required to be globally
routable unicast, and those addresses pinned into the HTTP client so a
second lookup cannot rebind to a private one. A request for
http://127.0.0.1:8080/a.jpgcomes back403 request blocked. - No redirects. The redirect policy is
none(), so a public URL cannot bounce the fetcher inward. - Magic-byte validation, backed by a decoder-format allowlist, so a polyglot file cannot be routed to a decoder other than the one its header declares.
- Source size capped at 20 MiB, checked against
Content-Lengthand again while streaming, because the header can be absent or can lie. - Decompression bomb protection. Declared width times height times the decoder’s real bytes per pixel must fit under 50 MiB, checked before any pixel buffer is allocated.
- Transforms run off the async workers behind a concurrency limit, so a burst of large images cannot starve the runtime.
The rate limiter is an in-memory fixed-window counter. It resets on restart and
is not shared between instances, so treat it as a backstop and do real rate
limiting at the proxy.
Caching
Two levels, both keyed on the exact(src, w, f) triple:
- an in-memory LRU, 512 entries by default,
- a disk cache under
--cache-dir, with a background sweeper that evicts expired entries and then the oldest ones to stay under--max-disk-cache-bytes(2 GiB by default).
Next
Components
Props, scope, slots, and imports.
Image service reference
Every parameter, limit and status code.
Security model
The order the guards run in, and what they do not cover.
Dev server
What the dev server serves.