Skip to main content
Signed URLs add a time limit — and optionally a country restriction or an IP lock — to file and video links, so they can’t be shared or reused forever. They work for both Storage and Stream, as long as the matching tokenSecurityKey is set.
payload.config.ts
Signed URLs secure downloads (how files are served). For securing uploads — minting short-lived URLs so the browser can send files straight to Bunny — see Client uploads.

Options

Getting your token security keys

Bunny dashboard → DeliveryCDN → your Pull Zone → SecurityToken Authentication → enable it and copy the key.

Requirements

  • If signedUrls is enabled anywhere storage is configured, storage.tokenSecurityKey is required.
  • If signedUrls is enabled anywhere stream is configured, stream.tokenSecurityKey is required.
Without the matching key, the plugin’s config validator throws a startup error.
The tokenSecurityKey is per zone / per library. When a collection points at its own storage zone or stream library, that zone/library must carry its own tokenSecurityKey for signed URLs to work on it — the global key isn’t inherited.
A Bunny token covers the query string, so any parameter added after signing invalidates it. Payload’s upload.cacheTags appends the document’s updatedAt to admin thumbnails, which would break those links — the plugin therefore sets cacheTags: false on collections that hand signed CDN URLs straight to the browser (disablePayloadAccessControl: true). Filenames are already unique per upload, so cache busting isn’t needed there.

Proxy vs. redirect (staticHandler)

When a collection uses Payload’s access control (the default), a file request goes through Payload first. staticHandler controls what happens next:
Redirect is recommendedProxying downloads the file from Bunny to your server and back out to the user on every request — a redirect sends the user straight to Bunny’s CDN, so your server does none of that work. It also lets Stream videos play as HLS instead of falling back to MP4 — see Access control and mp4Fallback.

IP locking (userIp)

userIp binds each signed link to a single client IPv4 address — a leaked URL stops working from any other IP. You provide the callback because reliably extracting the real client IP depends on your host and proxy chain:
How it behaves:
  • The IP goes into the token hash only — it is never added to the URL.
  • Return a falsy value when no IP can be determined; the link is then signed without an IP lock rather than failing the request.
  • The callback runs only for URLs the client itself will open — document url fields, admin thumbnails, and staticHandler redirects. URLs your server fetches on the client’s behalf (proxied downloads) and URLs generated outside a request (cache purging) are never IP-locked, since Bunny would see your server’s IP there, not the client’s.
Bunny-side requirements
  • Enforcement requires Token IP Validation to be enabled on Bunny: Pull Zone → SecurityToken Authentication for Storage, or the equivalent setting on your Stream library. Without it, Bunny ignores the IP in the hash — but a token signed with an IP won’t validate, so only enable userIp together with the Bunny-side setting.
  • Only IPv4 is supported; enabling Token IP Validation disables IPv6 routing on the Pull Zone. Values that aren’t a plain IPv4 address are ignored (the link is signed without a lock) and a warning is logged.
Clients behind carrier-grade NAT or switching networks (mobile) can legitimately change IPs mid-session, which breaks IP-locked links. Prefer short expiresIn values over IP locking for those audiences, or combine both with a tolerant userIp callback.

Absolute expiry (expiresAt)

expiresIn is a sliding window from the moment each URL is signed. expiresAt sets a fixed cut-off instead — useful when links must all die at the same wall-clock moment, such as the end of a live event or a rental period:
Return a Date or a UNIX timestamp in seconds. Return undefined to fall back to expiresIn for that URL. The callback receives the same { collection, filename, req? } args as shouldUseSignedUrl.

Example: sign only videos, restrict by country

Collection overrides

See Collection overrides for the full list of options you can tune per collection.