← Back to Blog

Your API keys are in your HTML and anyone can read them

Your API keys are in your HTML and anyone can read them

View source on a surprising number of production websites and you'll find API keys sitting in plain text. Stripe publishable keys mixed in with Stripe secret keys. AWS access key IDs paired with their secret counterparts. OpenAI API keys embedded in client-side JavaScript that anyone can copy with a right-click.

This isn't a theoretical risk. Automated scrapers continuously scan public websites and GitHub repositories for leaked secrets. When they find an AWS key, they spin up cryptocurrency mining instances within minutes. When they find a Stripe secret key, they can issue refunds or access customer payment data. A leaked OpenAI key can rack up thousands in API charges before the owner notices.

How secrets end up in frontend code

The most common path is a developer who uses environment variables locally but hardcodes values for a quick test, then forgets to remove them before deploying. The second most common is a build process that injects server-side environment variables into client-side bundles without filtering. The third is copy-pasting configuration snippets from documentation without understanding which values are safe to expose publicly and which are not.

Some keys are designed to be public. Stripe publishable keys, Google Maps client-side API keys with proper referrer restrictions, and reCAPTCHA site keys are all meant to appear in frontend code. The danger is when server-side secrets get mixed in with these public keys.

The pattern list

The API Secret Leakage Audit scans your page's HTML and linked JavaScript files for over 20 known secret patterns:

  • Cloud providers: AWS access key IDs (AKIA...), Azure connection strings, GCP service account JSON
  • Payment processors: Stripe secret keys (sk_live_...), PayPal client secrets
  • Communication platforms: Twilio auth tokens, SendGrid API keys, Mailgun private keys
  • AI services: OpenAI API keys (sk-...), Anthropic API keys
  • Developer tools: GitHub personal access tokens (ghp_...), GitLab tokens, Slack webhook URLs, Slack bot tokens
  • Authentication: JWTs in page source, private RSA/EC keys, base64-encoded credentials
  • Databases: MongoDB connection strings with credentials, PostgreSQL connection URIs with passwords

Each pattern uses the actual format of the secret (prefix patterns, character sets, lengths) to minimize false positives. A random string that happens to start with sk_ won't trigger a flag unless it matches the full Stripe secret key format.

What to do when you find a leak

If the audit finds a real secret in your frontend code:

  1. Rotate the key immediately. Don't just remove it from the code. The moment a secret appears in page source, assume it's been scraped. Generate a new key in the provider's dashboard and update your server-side configuration.

  2. Check your provider's audit logs. Most cloud and API providers log every request made with each key. Look for unauthorized usage between when the key was deployed and when you rotated it.

  3. Move the secret server-side. If the key is needed for an API call, that call should happen on your server, not in the browser. Use a serverless function or API route as a proxy between your frontend and the third-party API.

  4. Add secrets scanning to your deploy process. Tools like git-secrets, truffleHog, or your CI provider's built-in secret scanning can catch leaked keys before they reach production.

The one thing you should not do is assume nobody noticed. Automated scanners index public web pages continuously. If the key was live for even a few hours, treat it as compromised.

If you're building API integrations for the first time and want to avoid these mistakes from the start, The $97 Launch covers the proxy pattern for safely calling third-party APIs from static sites.

Fact-check notes and sources

Related reading

This post is informational, not security-consulting or legal advice. Mentions of AWS, Stripe, OpenAI, and other third parties are nominative fair use. No affiliation is implied.

← Back to Blog

Accessibility Options

Text Size
High Contrast
Reduce Motion
Reading Guide
Link Highlighting
Accessibility Statement

J.A. Watte is committed to ensuring digital accessibility for people with disabilities. This site conforms to WCAG 2.1 and 2.2 Level AA guidelines.

Measures Taken

  • Semantic HTML with proper heading hierarchy
  • ARIA labels and roles for interactive components
  • Color contrast ratios meeting WCAG AA (4.5:1)
  • Full keyboard navigation support
  • Skip navigation link
  • Visible focus indicators (3:1 contrast)
  • 44px minimum touch/click targets
  • Dark/light theme with system preference detection
  • Responsive design for all devices
  • Reduced motion support (CSS + toggle)
  • Text size customization (14px–20px)
  • Print stylesheet

Feedback

Contact: jwatte.com/contact

Full Accessibility StatementPrivacy Policy

Last updated: April 2026