Overview
By default, the/t/* route is public — anyone who knows the URL can request a transformation. Signed URLs let you lock that down. Each signed URL carries a cryptographic signature that the server verifies before serving the asset. Requests with a missing or invalid signature are rejected with 401 Unauthorized.
This is useful when you want to:
- Prevent hotlinking or unauthorized transformation of private assets
- Control which transformations are allowed for a given file
- Ensure only your backend can generate valid delivery URLs
How it works
Signed URLs use HMAC-SHA256 with a shared secret (API_SECRET) to produce a 16-character signature. The signature is embedded in the URL path. On every request the server recomputes the expected signature and compares it using a timing-safe operation.
{transformations}/{file-path}when a transformation string is present{file-path}alone when no transformation is applied
Setup
1. Set API_SECRET
Add API_SECRET to your environment. It must be at least 16 characters long.
2. Generate signatures in your backend
Usecrypto.createHmac (Node.js) or an equivalent library in your language. Never generate signatures client-side.
- TypeScript / Node.js
- Python
- PHP
URL format reference
| Part | Description |
|---|---|
s--{signature} | 16-character HMAC-SHA256 prefix. Always starts with s--. |
{transformations} | Optional. Same comma-separated params as /t/* (e.g. w_800,h_600,f_webp). |
{file-path} | Path to the file relative to your storage root (e.g. uploads/photo.jpg). |
Examples
Error responses
| Status | Cause |
|---|---|
400 | Malformed URL (missing signature prefix, wrong segment count) |
401 | Signature is valid in format but does not match the expected HMAC |
500 | API_SECRET is not configured on the server |
Security considerations
Signatures are tied to exact transformations and paths
Signatures are tied to exact transformations and paths
Changing any character in the transformation string or file path invalidates the signature. A signature for
w_800,h_600/photo.jpg cannot be reused for w_400,h_300/photo.jpg.Timing-safe comparison prevents timing attacks
Timing-safe comparison prevents timing attacks
The server uses
crypto.timingSafeEqual to compare signatures. This prevents attackers from deducing valid signatures by measuring response time differences.The signature does not expire
The signature does not expire
Signed URLs do not have a built-in expiry. If you need time-limited URLs, append an expiry timestamp to the file path or transformation string and enforce it in a reverse proxy or middleware layer.
Rotate API_SECRET if compromised
Rotate API_SECRET if compromised
If
API_SECRET is leaked, immediately replace it with a new value and restart the server. All previously generated signed URLs will become invalid.Related
Image Transformations
Full reference for transformation parameters you can sign.
Server Configuration
How to set
API_SECRET and other server options.