Signed URLs
Signed URLs attach an HMAC-SHA256 signature (and an optional expiration timestamp) as query parameters to a URL. This lets you share links that cannot be tampered with — if anyone modifies the path, query string, or expiration, the signature check fails.
Common use cases include temporary download links, email verification, password reset flows, and shareable preview links.
Prerequisites
Section titled “Prerequisites”Set an APP_SECRET environment variable in your Worker. This value is used as the HMAC signing key.
[vars]APP_SECRET = "your-secret-key"Generating signed URLs
Section titled “Generating signed URLs”Call ctx.signedUrl() from any route handler to produce a signed link to a named route. The method accepts the route name, route parameters, and an options object.
import { Controller, Route, type RouterContext } from 'stratal/router'
@Controller('/files')class FilesController { @Route({ name: 'files.download' }) async show(ctx: RouterContext) { const url = await ctx.signedUrl('files.download', { id: '123' }, { expiresIn: 3600, // 1 hour }) return ctx.json({ downloadUrl: url }) }}Options
Section titled “Options”| Option | Type | Description |
|---|---|---|
expiresIn | number | Time in seconds until the signed URL expires. Omit for a non-expiring link. |
Verifying signatures
Section titled “Verifying signatures”Use ctx.hasValidSignature() to check whether the current request URL carries a valid, non-expired signature.
@Route({ name: 'files.download' })async show(ctx: RouterContext) { const valid = await ctx.hasValidSignature() if (!valid) { return ctx.json({ error: 'Invalid or expired link' }, 403) } // serve the file...}The method returns false if the signature is missing, does not match, or the link has expired.
Standalone functions
Section titled “Standalone functions”If you need to sign or verify URLs outside of a route handler — for example in a queue consumer or scheduled task — use the standalone helpers.
import { signUrl, verifySignedUrl } from 'stratal/router'
const signed = await signUrl('https://example.com/download/123', secret, { expiresIn: 3600,})
const isValid = await verifySignedUrl(signed, secret)