Image Processing
PicFast offers two layers of image processing. Choose based on whether you need to transform a single image on demand or apply a fixed policy to every upload:
- On-the-fly — append
@+ params to any image URL; processed on the CDN/browser cache layer. Good for one-off resizes or a gallery that serves the same source at multiple sizes. - Upload-time — the server transforms and stores the result at upload time. Good for consistent compression, metadata stripping, and watermarking before storage hits the network.
On-the-fly (URL parameters)
Append @ followed by comma-separated parameters to an image URL. The result is cached and served on subsequent requests.
Quick examples
# Original
https://picfast.example.com/i/abc123.jpg
# Resize to width 300px
https://picfast.example.com/i/abc123.jpg@w_300
# Resize to height 200px
https://picfast.example.com/i/abc123.jpg@h_200
# Fit within 300×200 box (keeps aspect ratio)
https://picfast.example.com/i/abc123.jpg@w_300,h_200
# Compress to quality 80
https://picfast.example.com/i/abc123.jpg@q_80
# Convert to WebP
https://picfast.example.com/i/abc123.jpg@f_webp
# Combine all
https://picfast.example.com/i/abc123.jpg@w_300,h_200,q_60,f_webp Parameters
| Param | Description | Range |
|---|---|---|
w_ | Target width (px) | 1 – 10000 |
h_ | Target height (px) | 1 – 10000 |
q_ | Output quality | 1 – 100 |
f_ | Output format | jpeg, png, webp, gif |
Behavior
- When both
w_andh_are set, the image is scaled to fit within the box while preserving aspect ratio (no crop). - GIF images are not processed and returned as-is.
- If processing fails for any reason, the original image is returned unchanged.
- Processed responses use
Cache-Control: public, max-age=2592000(30 days). CDN or browser caches will serve the processed version without hitting PicFast again.
Upload-time (server-side)
When a user uploads an image, the server applies the configured processing pipeline before storing the result. The original is discarded; only the processed version is kept.
User settings
Each user can configure their own pipeline from the gear icon on the upload page. Settings persist on the server and apply to all subsequent uploads.
| Setting | Description | Default |
|---|---|---|
| Compression quality | JPEG/WebP quality, 1 – 100. Only applies when format is jpeg or webp. | 85 |
| Output format | origin keeps the source format; jpeg / png / webp transcodes. | origin |
| Strip EXIF | Removes all EXIF metadata (camera info, GPS, etc.) before storage. Recommended for privacy. | on |
| Text watermark | Overlays configurable text on every uploaded image. See watermark options below. | off |
Watermark options
| Option | Description |
|---|---|
| Text | The literal string to render. Required when watermark is enabled. |
| Position | top-left, top-right, center, bottom-left, bottom-right. |
| Font size | 8 – 200 (CSS px). |
| Color | Any CSS color value (e.g. #FFFFFF, oklch(...)). |
| Opacity | 0 – 1. |
Admin controls
Admins configure two global switches on the Access settings page that override per-user settings:
| Switch | Effect |
|---|---|
allow_user_image_processing | When off, every user is forced to the system default pipeline (quality 85, origin format, EXIF strip on, no watermark). The user gear icon still opens the dialog but the fields are disabled. |
skip_image_processing | When on, all uploads bypass the processing pipeline entirely. The original file is stored as-is. Useful for raw backups or trusted internal users. |
Precedence: skip_image_processing wins over user settings; otherwise the user pipeline runs, falling back to system defaults if the user has not configured anything.
Behavior
- Processing happens once, at upload, on the server (libvips). Network bandwidth to storage is reduced because only the processed file is uploaded.
- Originals are not retained. If a user wants the unprocessed source they must keep their own copy.
- Each setting has server-side validation (quality 1-100, format enum, font size 8-200, opacity 0-1). Invalid values are rejected on save with a 400.
- If processing fails for any reason, the upload still succeeds with the original file stored (graceful fallback).
Backend support
| Backend | On-the-fly | Upload-time | Notes |
|---|---|---|---|
| Local | Built-in | Built-in | Works out of the box |
| WebDAV | Built-in | Built-in | Works out of the box |
| S3 / OSS / COS / Kodo | Requires link_mode: "proxy" | Built-in | See Storage Backends for the on-the-fly requirement |
For cloud backends that return direct CDN URLs, you can alternatively use the cloud provider's native image processing parameters (e.g. Alibaba OSS ?x-oss-process=..., Qiniu ?imageView2/...) in place of PicFast's on-the-fly layer.
Image details & search
Every image in PicFast carries metadata that the admin panel and public image detail page can surface. Click an image to open its detail drawer.
EXIF metadata
PicFast extracts EXIF metadata (camera model, lens, exposure, GPS, etc.) from every uploaded JPEG and displays it in the image detail drawer. No configuration needed — the data is extracted server-side at upload time.
Perceptual Hash (pHash)
During upload, PicFast calculates a 64-bit Perceptual Hash (pHash) for supported image formats. Unlike cryptographic hashes (which change completely if a single pixel changes), pHash remains similar even if the image is resized, slightly cropped, or compressed. This data powers duplicate detection and visual similarity checks.
Tags
Images can be organized with tags. Tags are per-image and managed through the image detail drawer or the Tags admin page. Available operations:
- Create and delete tags from the admin Tags page
- Add or remove tags on any image from the detail drawer
- Filter images by tag in the image list
- Tag ownership and image visibility checks are enforced on all tag endpoints
Advanced search
The image list supports filtering beyond simple keyword search:
| Filter | Description |
|---|---|
| Keyword | Case-insensitive ILIKE match against image filenames |
| Extension | Filter by file type (png, jpg, webp, gif, etc.) |
| Date range | Filter images uploaded within a start and end date |
| Tags | Filter images that have specific tags assigned |
OCR text
PicFast reserves an ocr_text column on every image row. When an OCR processor is configured, extracted text is stored here and displayed in the image detail drawer. Built-in OCR processing is gated behind app.enable_dummy_ocr (off by default) and capped at 5 concurrent goroutines — swap in a real OCR provider for production use.