urltoolskit.org
URL utilities, in the browser
Say hi →

Signed URL builder (HMAC)

sign and verify · SHA-256 / 384 / 512 · expiry parameter · WebCrypto, nothing uploaded

A signed URL is one nobody can tamper with: change a parameter and the signature no longer matches. Build one here, or paste an existing signed URL to check why it is being rejected — wrong secret, wrong canonical form, or simply expired. Signing uses the browser's WebCrypto, so the secret stays in the tab.

Verification

Ready.

How to use

  1. Paste the URL and enter the shared secret. Both sides of the exchange must use the same one.
  2. Pick What gets signed to match your server. This is the setting that decides whether verification succeeds, and the Canonical string box shows exactly what was hashed so you can compare it against your server's implementation.
  3. Set Expires in to add an expiry parameter. It is added before signing, so it cannot be extended by editing the URL.
  4. Switch Mode to Verify and paste a signed URL to check it. The verification table reports whether the signature matches and whether the expiry has passed.
  5. Copy the signed URL.

Why verification fails when the secret is right

Nearly always because the two sides disagree on the canonical string. HMAC hashes bytes, so signing /file.pdf?a=1&b=2 and verifying /file.pdf?b=2&a=1 produces different signatures even though the URLs are equivalent. That is why Path + query, parameters sorted is the default — sorting makes parameter order irrelevant, which matters because proxies, redirects and client libraries do reorder query strings.

The other mismatches worth checking: whether the scheme and host are included (they break the moment you move behind a CDN or switch to HTTPS), whether the signature parameter itself is excluded from the input (it must be — it does not exist yet at signing time, and this tool always removes it), and whether values are compared encoded or decoded. Get the canonical form written down as a spec, on both sides, before debugging anything else.

Two security notes. Verify signatures with a constant-time comparison on the server — a plain string equality check leaks timing information that can, in principle, be used to forge one. And always include an expiry: a signed URL without one is a permanent credential that will end up in a log file, a referrer header or someone's bookmarks. This tool signs and verifies for development and debugging; it is not a key management system, and a production secret pasted into any web page is a secret you should rotate.

FAQ

Does my secret get sent anywhere?

No. crypto.subtle is built into the browser and the signing happens in the tab — the Network panel stays empty. The field is a password input so it is not shoulder-readable, and nothing is stored.

Is this compatible with AWS CloudFront or S3 signed URLs?

No. Those use their own canonicalisation (SigV4 for S3, an RSA signature and a policy document for CloudFront) and cannot be reproduced with a plain HMAC. This implements the common "sign the path and query with a shared secret" pattern used by Cloudflare, imgproxy, Thumbor-style services and most in-house implementations.

hex or base64url?

base64url is shorter — 43 characters instead of 64 for SHA-256 — and safe in a URL without further escaping. hex is easier to eyeball and to compare in logs. Match whatever the server expects; they carry the same information.

Should the expiry be a Unix timestamp?

That is what is generated here: seconds since the epoch, which is what almost every implementation uses and what the verify mode reads back and renders as a readable date. Milliseconds and ISO-8601 both exist in the wild, so check your server's format.