Secrets
Encrypted-at-rest API keys and tokens for your site's worker — never reach the browser
Secrets are encrypted-at-rest values (API keys, webhook secrets,
tokens) accessible inside your site's worker function
as env.NAME. Values are decrypted only when the worker boots; they
never appear in the published payload and never reach the browser.
Distinct from shiply Variables: Variables are encrypted at rest too, but are available to both proxy routes and worker functions. Secrets are scoped strictly to the worker.
Declare in .shiply/secrets.json
List the secret names your worker expects. Values are set out-of-band via the CLI / MCP / REST — they never appear in the publish payload:
{
"secrets": ["STRIPE_WEBHOOK_SECRET", "STRIPE_SECRET_KEY", "RESEND_API_KEY"]
}On shiply publish, the CLI prompts you for any declared secret that
isn't set yet (and MCP returns a hint in the publish response). The
file is optional — you can set secrets purely via CLI / MCP without
declaring them — but it's the easiest way to document what your worker
needs.
Set a value
Three equivalent ways:
shiply secret set <slug> STRIPE_SECRET_KEY sk_live_xxxMCP:
set_secret(slug, "STRIPE_SECRET_KEY", "sk_live_xxx")REST:
POST /api/v1/sites/{slug}/secrets
{ "name": "STRIPE_SECRET_KEY", "value": "sk_live_xxx" }All three require Authorization: Bearer shp_….
Use in your worker
import Stripe from 'stripe'
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const stripe = new Stripe(env.STRIPE_SECRET_KEY)
// ...
},
}Cloudflare auto-injects the binding when the worker boots. Values
never reach the browser — even if you accidentally console.log
them, the log lands in CF logs, not the response.
Lifecycle
- Secrets persist across function redeploys — once set, they stay set until you remove them or delete the whole function.
shiply secret ls <slug>lists names (values are never returned).shiply secret rm <slug> NAMEremoves one.shiply function rm <slug>cascades — removing the function deletes all its secrets and crons.
Naming + limits
- Names must match
[A-Z_][A-Z0-9_]*(uppercase + digits + underscores; can't start with a digit). - Max 80 chars per name.
- Max 50 secrets per site.
Values are unbounded in length, but Cloudflare rejects values above ~64 KB at the secret-set step.