Secure your ogis instance with HMAC-SHA256 signature validation. When enabled, all requests must include a valid signature parameter.
When to Use Authentication
- Private instances — Prevent unauthorized usage of your self-hosted ogis
- Rate limiting — Control who can generate images
- Usage tracking — Identify requests by signature
The public hosted service at img.ogis.dev does not require authentication.
Enabling Authentication
Set the OGIS_HMAC_SECRET environment variable on your server:
docker run -d \
-p 3000:3000 \
-e OGIS_HMAC_SECRET=your-secret-key-here \
twango/ogis:latest
ogis --hmac-secret your-secret-key-here
Choose a strong, random secret (32+ characters recommended):
# Generate a secure secret
openssl rand -hex 32
How It Works
- Client constructs query parameters (e.g.,
title=Hello&template=twilight)
- Parameters are sorted alphabetically and concatenated
- HMAC-SHA256 signature is computed using the secret
- Signature is appended to the URL as
&signature=...
- Server verifies the signature before generating the image
Using the SDK
The SDK handles signing automatically when you provide hmacSecret:
import { OgisClient } from 'ogis';
const ogis = new OgisClient({
baseUrl: 'https://ogis.example.com',
hmacSecret: process.env.OGIS_SECRET // Keep this secret!
});
// URLs are automatically signed
const url = ogis.generateUrl({
title: 'My Secure Image',
template: 'twilight'
});
// => https://ogis.example.com/?template=twilight&title=My+Secure+Image&signature=a1b2c3...
Manual Signing
If you’re not using the SDK, compute the signature manually.
Algorithm
- Collect all query parameters except
signature
- Sort parameters alphabetically by key
- URL-encode and concatenate as
key=value&key=value
- Compute HMAC-SHA256 of the string using your secret
- Hex-encode the result
Implementation Examples
import crypto from 'crypto';
function signOgisUrl(params: Record<string, string>, secret: string): string {
// Sort parameters alphabetically
const sortedKeys = Object.keys(params).sort();
// Build canonical query string
const canonical = sortedKeys
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&');
// Compute HMAC-SHA256
const signature = crypto
.createHmac('sha256', secret)
.update(canonical)
.digest('hex');
return signature;
}
// Usage
const params = { title: 'Hello', template: 'twilight' };
const signature = signOgisUrl(params, 'your-secret');
const url = `https://ogis.example.com/?title=Hello&template=twilight&signature=${signature}`;
import hmac
import hashlib
from urllib.parse import urlencode
def sign_ogis_url(params: dict, secret: str) -> str:
# Sort and encode parameters
sorted_params = sorted(params.items())
canonical = urlencode(sorted_params)
# Compute HMAC-SHA256
signature = hmac.new(
secret.encode(),
canonical.encode(),
hashlib.sha256
).hexdigest()
return signature
# Usage
params = {'title': 'Hello', 'template': 'twilight'}
signature = sign_ogis_url(params, 'your-secret')
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"net/url"
"sort"
)
func signOgisURL(params map[string]string, secret string) string {
// Sort keys
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
// Build canonical string
values := url.Values{}
for _, k := range keys {
values.Set(k, params[k])
}
canonical := values.Encode()
// Compute HMAC-SHA256
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(canonical))
return hex.EncodeToString(h.Sum(nil))
}
Error Responses
When authentication is enabled, invalid requests return:
| Status | Reason |
|---|
401 Unauthorized | Missing signature parameter |
401 Unauthorized | Invalid signature |
Security Best Practices
Never expose your secret in client-side code or version control.
- Use environment variables — Store secrets in
OGIS_SECRET or similar
- Rotate secrets periodically — Update your secret and redeploy
- Use HTTPS — Always serve your ogis instance over HTTPS to prevent signature interception
Server-Side Only
Generate signed URLs on your server, not in the browser:
Next.js API Route
SvelteKit
// pages/api/og-image.ts
import { OgisClient } from 'ogis';
const ogis = new OgisClient({
baseUrl: process.env.OGIS_URL!,
hmacSecret: process.env.OGIS_SECRET!
});
export default function handler(req, res) {
const { title } = req.query;
const url = ogis.generateUrl({ title, template: 'twilight' });
res.json({ url });
}
// +page.server.ts
import { OgisClient } from 'ogis';
import { OGIS_SECRET } from '$env/static/private';
const ogis = new OgisClient({
baseUrl: 'https://ogis.example.com',
hmacSecret: OGIS_SECRET
});
export function load({ params }) {
return {
ogImage: ogis.generateUrl({ title: params.title })
};
}