Zudo Slack Wisdom
GitHub repository

Type to search...

to open search from anywhere

Secrets and Config

Setting SLACK_BOT_TOKEN and SLACK_SIGNING_SECRET as Worker secrets, never as vars or client-bundled values.

Overview

A Slack integration needs exactly two confidential values: the bot token (SLACK_BOT_TOKEN, xoxb-…) used to call the Web API, and the signing secret (SLACK_SIGNING_SECRET) used to verify inbound requests (see Verifying Requests). Both belong to the Worker as secrets, set with wrangler secret put — never as vars in wrangler.toml, and never referenced anywhere a bundler could pull them into client-shipped JS.

wrangler secret put

npx wrangler secret put SLACK_BOT_TOKEN
npx wrangler secret put SLACK_SIGNING_SECRET

Each command prompts for the value and pushes it straight to the deployed Worker; it is never written to wrangler.toml or any file in the repo. Per Cloudflare's secrets docs, wrangler secret put creates a new Worker version and deploys it immediately — for a gradual/staged rollout, wrangler versions secret put creates the version without deploying it.

Never put a Slack token or signing secret in vars

wrangler.toml's [vars] block is plaintext, checked into the repo, and visible in the Cloudflare dashboard. Cloudflare's own guidance is explicit:"Do not use vars to store sensitive information in your Worker's Wrangler configuration file. Use secrets instead."SLACK_BOT_TOKEN and SLACK_SIGNING_SECRET are exactly the kind of value that rule is for.

Local dev: .dev.vars

wrangler dev reads local-only secrets from a .dev.vars file next to wrangler.toml, using dotenv formatting:

SLACK_BOT_TOKEN="xoxb-your-dev-workspace-token"
SLACK_SIGNING_SECRET="your-dev-workspace-signing-secret"
# .gitignore
.dev.vars*

.dev.vars (and .env, if used) must never be committed — the same file that makes local dev convenient is a plaintext secret if it lands in git history. Cloudflare also supports .dev.vars.<environment-name> for per-environment local overrides, which load before the generic .dev.vars; the glob above covers those variants too — a bare .dev.vars entry does not.

Typed Env

Declare both secrets on the Env interface so every handler gets compile-time checking instead of stringly-typed env["SLACK_BOT_TOKEN"] lookups:

export interface Env {
  SLACK_BOT_TOKEN: string;
  SLACK_SIGNING_SECRET: string;
  // ... other bindings: KV, D1, etc.
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    // env.SLACK_BOT_TOKEN and env.SLACK_SIGNING_SECRET are typed strings here
    return await handleRequest(request, env, ctx);
  },
} satisfies ExportedHandler<Env>;

If the project generates types from wrangler.toml (wrangler types), the generated Env already includes secrets declared there implicitly — but since secrets are never declared in wrangler.toml (only vars and bindings are), hand-writing the Env fields above is the normal path for Slack secrets specifically.

Rotating a leaked or expiring token

Re-running wrangler secret put SLACK_BOT_TOKEN overwrites the old value and deploys immediately — there is no separate "revoke" step on the Worker side. Rotate the token in Slack's app management console first (or immediately after, if the old one is confirmed leaked), then push the new value with the same command.

Gotchas

  • vars and secrets look identical in codeenv.SLACK_BOT_TOKEN reads the same either way. The difference is entirely in how the value got there; get the declaration right, because the code won't warn you.

  • .dev.vars values never reach production — they exist only for wrangler dev. Forgetting to also run wrangler secret put after adding a new secret to .dev.vars is a common "works locally, 500s in prod" gap.

  • A secret set via the dashboard and one set via CLI both work identically — but only one should be treated as the source of truth per project, or the two can silently drift out of sync across environments.

  • A "preview" deployment can still see production secrets. Unless CI explicitly deploys preview builds to a separate Worker environment with its own secrets, a preview version of the same Worker shares production bindings — including secrets. Don't assume a PR preview is a sandboxed workspace token unless a separate environment was actually set up for it.

Revision History

CreatedUpdated