Skip to content

Signature

Signed URLs allow you to give time-limited access to your media endpoints without exposing permanent credentials. The server verifies that the URL was signed with a valid private key and that the timestamp (ts) has not expired.


Generate a signature key pair for your Space in the security configuration:

Generate keys

  • Store the private key securely – it will not be returned again. The server uses the public key to validate requests signed by your private key.

Keys

To enable signature protection for all your transformations, navigate to the Transformations section in the security configuration and set Level of security to Signature or token

Signature level

Once this setting is applied, a signature or Bearer token will be required for transformation requests.

You can configure how long a signature remains valid.

To change the default value (5 minutes), go to Signature Expiry and set a custom duration (up to 60 days).

Expiry

To call a protected endpoint:

  1. Compute a timestamp (seconds since epoch):
ts = floor(now / 1000)
  1. Build the unsigned URL including ts and any parameters (but without signature). Example:
/demo/media/crab.jpg?ts=1732812345&w=800
  1. Create the normalized string:
"{HTTP_METHOD} {PATH_AND_QUERY}".toLowerCase()

"get /demo/media/crab.jpg?ts=1732812345&w=800"

  1. Sign the normalized string with ECDSA P-256 + SHA-256 using your private key.

  2. Base64URL-encode the signature (replace +-, /_, trim =).

  3. Append the signature to your URL:

?ts=1732812345&w=800&signature=BASE64URL

✅ Done — you can now call the signed URL.


Terminal window
curl "https://media.pixel-fiddler.com/demo/media/crab.jpg?ts=1732812345&w=800&signature=BASE64URL_SIG"

If a request is made without a signature, the backend will return HTTP 401:

{
"type": "pf:problems/SignatureMissing",
"title": "Signature is missing",
"status": 401,
"detail": "Resource '/demo/media/crab.jpg' is secured with signature. (https://pixel-fiddler.com/docs/security/signature)",
"instance": "/demo/media/crab.jpg"
}

If the signature is invalid or the timestamp has expired, the backend will return HTTP 403:

{
"type": "pf:problems/SignatureExpired",
"title": "Signature expired",
"status": 403,
"detail": "Signature for resource '/demo/media/crab.jpg' expired. (https://pixel-fiddler.com/docs/security/signature)",
"instance": "/demo/media/crab.jpg"
}

import java.util.*
import java.security.*
import java.security.spec.PKCS8EncodedKeySpec
import java.time.Instant
import java.net.URL
fun signUrl(method: String, url: URL, privateKeyB64: String): String {
val pathWithQuery = url.path + (url.query?.let { "?$it" } ?: "")
val normalized = "$method $pathWithQuery".lowercase()
val keyBytes = Base64.getDecoder().decode(privateKeyB64)
val pk = KeyFactory.getInstance("EC")
.generatePrivate(PKCS8EncodedKeySpec(keyBytes))
val sig = Signature.getInstance("SHA256withECDSA")
sig.initSign(pk)
sig.update(normalized.toByteArray(Charsets.UTF_8))
val der = sig.sign()
val signature = Base64.getUrlEncoder().withoutPadding().encodeToString(der)
return "$url&signature=$signature"
}
fun main() {
val url = URL("https://media.pixel-fiddler.com/demo/media/crab.jpg?ts=${Instant.now().epochSecond}")
val signedUrl = signUrl("GET", url, "<YOUR_PRIVATE_KEY_BASE64>")
println(signedUrl)
}

  • Keep ts validity window short (minutes to hours).
  • Always exclude signature when signing.
  • Lowercase the full METHOD path?query.
  • Store your private key securely – never share it.
  • Rotate keys periodically using Regenerate key.

  • Use the private key to sign requests.
  • Send ts and signature as query parameters.
  • The server verifies with the public key and rejects invalid/expired signatures.
  • This mechanism ensures your media URLs are secure and short-lived.