A Caddy HTTP handler plugin that validates HMAC-signed URLs. Perfect for creating secure access to protected resources like file downloads or API endpoints.
- π HMAC signature verification β Industry-standard cryptographic signing
- π― Flexible algorithm choice β Supports multiple HMAC algorithms
- π URL-safe signatures β Uses raw URL-safe Base64 encoding
Build Caddy with this plugin using xcaddy:
xcaddy build --with github.com/hookenz/caddy-signed-urlsOr add it to your go.mod:
go get github.com/hookenz/caddy-signed-urlssigned_url "your-secret-key"signed_url {
secret "your-secret-key" # Required
algorithm "sha256" # Optional, default: sha256
}| Option | Type | Default | Description |
|---|---|---|---|
secret |
string | (required) | Secret key for HMAC signing |
algorithm |
string | sha256 |
The HMAC algorithm to use |
package main
import (
"crypto/hmac"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"fmt"
"hash"
)
func generateSignedURL(secret, path, algorithm string) string {
var h hash.Hash
switch algorithm {
case "sha256":
h = hmac.New(sha256.New, []byte(secret))
case "sha384":
h = hmac.New(sha512.New384, []byte(secret))
case "sha512":
h = hmac.New(sha512.New, []byte(secret))
default:
panic("unsupported algorithm")
}
h.Write([]byte(path))
signature := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
return fmt.Sprintf("%s?signature=%s", path, signature)
}
func main() {
secret := "your-secret-key"
path := "/downloads/file.pdf"
url := generateSignedURL(secret, path, "sha256")
fmt.Println(url)
}import hmac
import hashlib
import base64
def generate_signed_url(secret, path, algorithm="sha256"):
hash_map = {
"sha256": hashlib.sha256,
"sha384": hashlib.sha384,
"sha512": hashlib.sha512
}
if algorithm not in hash_map:
raise ValueError("Unsupported algorithm")
sig = hmac.new(secret.encode(), path.encode(), hash_map[algorithm]).digest()
signature = base64.urlsafe_b64encode(sig).rstrip(b"=").decode()
return f"{path}?signature={signature}"
url = generate_signed_url("your-secret-key", "/downloads/file.pdf")
print(url)const crypto = require('crypto');
function base64UrlEncode(buffer) {
return buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
function generateSignedURL(secret, path, algorithm = 'sha256') {
const algoMap = { sha256: 'sha256', sha384: 'sha384', sha512: 'sha512' };
if (!algoMap[algorithm]) throw new Error("Unsupported algorithm");
const sig = crypto.createHmac(algoMap[algorithm], secret).update(path).digest();
const signature = base64UrlEncode(sig);
return `${path}?signature=${signature}`;
}
console.log(generateSignedURL("your-secret-key", "/downloads/file.pdf"));<?php
function generateSignedURL($secret, $path, $algorithm = 'sha256') {
$algoMap = ['sha256'=>'sha256', 'sha384'=>'sha384', 'sha512'=>'sha512'];
if (!isset($algoMap[$algorithm])) throw new Exception("Unsupported algorithm");
$sig = hash_hmac($algoMap[$algorithm], $path, $secret, true);
$signature = rtrim(strtr(base64_encode($sig), '+/', '-_'), '=');
return $path . '?signature=' . $signature;
}
echo generateSignedURL('your-secret-key', '/downloads/file.pdf');
?>files.example.com {
route /secure/* {
signed_url {
secret "super-secret-key"
algorithm "sha256"
}
file_server {
root /var/www/secure-files
}
}
route /public/* {
file_server {
root /var/www/public-files
}
}
}api.example.com {
route /api/private/* {
signed_url {
secret "api-secret-key"
algorithm "sha256"
}
reverse_proxy localhost:8080
}
route /api/public/* {
reverse_proxy localhost:8080
}
}- Use strong secrets β Minimum 32 characters, randomly generated
- Keep secrets secure β Never commit to version control
- Use HTTPS β Always transmit signed URLs over HTTPS to prevent interception
This plugin is licensed under the Apache License 2.0. See LICENSE for full text.