deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

Telegram bot tokens leak into client bundles via VITE_ and NEXT_PUBLIC_ env vars

Thousands of GitHub projects reference VITE_ or NEXT_PUBLIC_ Telegram bot token variables, which Vite and Next.js inline into JavaScript that every site visitor can download, a dev.to analysis finds.

Telegram bot tokens leak into client bundles via VITE_ and NEXT_PUBLIC_ env vars

A post on dev.to by Formgong, a form-backend vendor, documents a recurring security mistake: developers wiring contact forms to Telegram store the bot token in variables carrying Vite's VITE_ or Next.js's NEXT_PUBLIC_ prefix, and both prefixes compile the value into the JavaScript that every visitor downloads.

How the leak works

Both frameworks treat the prefixes as a signal that a value is public. According to the post, Lovable's documentation on secrets describes VITE_ values as browser-exposed at build time, and the Next.js documentation says NEXT_PUBLIC_ values are copied into the JavaScript shipped to the browser. The .env file can stay private and uncommitted; the build still writes the value into the output bundle.

The Formgong team tested this on a minimal Vite 8.3.1 app that read a token through import.meta.env.VITE_TELEGRAM_BOT_TOKEN, using a fake value. After a production build, the full fake token appeared in a JavaScript file under dist/assets. A second variable in the same .env file, without the prefix, appeared nowhere in the output.

How widespread it is

A GitHub code search on 9 November, reported in the post, counted 1,708 files mentioning VITE_TELEGRAM_BOT_TOKEN, 668 mentioning NEXT_PUBLIC_TELEGRAM_BOT_TOKEN and 237 with REACT_APP_TELEGRAM_BOT_TOKEN, the equivalent prefix from Create React App. Discord webhooks follow the same pattern: 489 files with VITE_DISCORD_WEBHOOK_URL and 191 with NEXT_PUBLIC_DISCORD_WEBHOOK_URL.

A mention does not prove a leak, so Formgong manually reviewed the first 100 results for each Telegram variable name. For the Vite version, 58 files were READMEs or setup notes telling readers to put their bot token in that variable, and 33 were code reading it, only 4 of which sat in a server folder. Seven of the 84 repositories were Lovable projects, identifiable by the lovable-tagger package in package.json. For NEXT_PUBLIC_TELEGRAM_BOT_TOKEN, 65 of the 100 files were code reading it; 12 were API routes or other server files, where the prefix alone is not a leak, but the remaining 53 were components or shared modules that reach the browser as soon as a client page imports them.

Committed tokens were rare: only 4 of the first 100 Vite files and 9 of the Next.js files contained anything shaped like a real token, because .env files are usually gitignored. The actual tokens sit in the JavaScript of deployed sites, where code search cannot see them but any visitor can.

Why developers fall into it

In a Vite app, an unprefixed environment variable reads as undefined in browser code, and the fastest way to make the error vanish is to add VITE_, which also publishes the value. Next.js has the same trap with NEXT_PUBLIC_.

The post also describes an informal experiment with ChatGPT. Asked plainly for an HTML contact form that sends messages to a Telegram bot, the assistant put the token in the page in 4 of 5 fresh chats, adding a warning only after the code. When the prompt mentioned Lovable, it proposed a Supabase Edge Function with the token stored as a secret in all 5 tries; when it mentioned Next.js on Vercel, it produced a server-side API route all 5 times, and 3 of those answers warned against NEXT_PUBLIC_ by name.

How to check and fix it

Formgong recommends opening the live site in browser DevTools and searching all sources for api.telegram.org, or discord.com/api/webhooks for Discord setups. A URL containing bot plus a long numeric identifier, or a variable holding it nearby, means the token is public. Anyone in that position should message @BotFather with /revoke; renaming the variable and redeploying is not enough, because whoever already downloaded the old bundle keeps the old token.

The fixes all move the token server-side. Lovable offers a Telegram connector whose gateway injects the token on the project's behalf, or an edge function with the secret kept out of .env. In Next.js, the token and chat ID belong in unprefixed environment variables read by a route handler; Formgong's example handler receives the form's FormData, validates and truncates fields, and discards submissions that fill a hidden honeypot field. The team also published bot-token-check, a build step that fails the deploy when a Telegram token or Discord webhook URL turns up in the build output. The authors also point, unsurprisingly, to their own form backend, which keeps the token out of the project altogether.

Why it matters

The failure is silent: the form works, the undefined error disappears, and nothing in the build logs hints that a credential has just been published. Because .env files normally stay out of git, neither GitHub search nor typical secret scanners will spot the token; it exists only in the deployed bundle, which every visitor downloads. A Telegram bot token grants full control of the bot, so exposure means anyone can operate it until the owner revokes. The lesson generalises beyond Telegram to any secret placed behind a public prefix, and the ChatGPT experiment suggests that generic prompts to AI assistants tend to produce the insecure version unless the question names a stack with a safe pattern.

  • #security
  • #vite
  • #next-js
  • #javascript
  • #telegram

Related posts