Uncategorized

How to Protect AI API Keys and Other Secrets on a Website Host

Screenshot of a WordPress dashboard interface.

How to Protect AI API Keys and Other Secrets on a Website Host

Answer box:
Store AI API keys, database passwords, webhook signing secrets and similar credentials server-side only: in your host’s secret store, a cloud secret manager, or server environment variables that are injected into the runtime. Do not put them in browser JavaScript, public repositories, theme files, client-side environment variables, screenshots, support tickets, or logs. For WordPress, prefer a plugin that reads a key from wp-config.php, an environment variable, or your host’s secret manager; if a plugin stores the key in the database, assume WordPress administrators and some plugins may be able to access it. If a key leaks, revoke or rotate it immediately, update every place that uses it, review logs and billing, and remove the exposed value from code, logs and tickets.

1. Know what counts as a secret

A secret is any value that lets software authenticate, sign, decrypt, impersonate, spend money, read private data, or change infrastructure. For an AI-assisted website, that usually includes:

  • AI provider API keys.
  • Database usernames, passwords and connection strings.
  • Webhook signing secrets for payments, forms, CRM tools or deployment hooks.
  • SMTP passwords and transactional email API tokens.
  • Object storage keys.
  • WordPress salts, application passwords and private plugin license keys when they unlock privileged features.
  • SSH private keys, deploy keys and CI/CD tokens.

Treat a secret as compromised if it appears in a Git commit, browser bundle, HTML source, JavaScript console, analytics payload, crash report, server log, debug file, screenshot, chat transcript, plugin support ticket or public paste. OpenAI’s current API documentation states that an API key is a secret, should not be shared or exposed in client-side code, and should be loaded from an environment variable or key management service on the server. (developers.openai.com)

The practical rule is simple: configuration that is safe for visitors to see can be public; credentials must stay on the server side.

2. Never put AI API keys in browser-side code

A browser is not a private place. Anything sent to the browser can be inspected by the visitor, copied by extensions, captured by proxies, cached, or discovered in built JavaScript files. That includes “hidden” fields, frontend framework variables, minified bundles and source maps.

OpenAI’s API key safety guidance says not to deploy keys in client-side environments such as browsers or mobile apps, and recommends routing requests through your own backend server instead. (help.openai.com) Next.js documentation gives a concrete example of why naming matters: variables prefixed with NEXT_PUBLIC_ are inlined into the JavaScript bundle delivered to the browser, while non-public variables remain available only in the Node.js environment. (nextjs.org)

A safe architecture looks like this:

  1. Visitor submits a form or chat message in the browser.
  2. Browser calls your own endpoint, such as /api/ai-reply.
  3. Your backend validates the request, rate-limits it and reads the AI key from a server-side secret.
  4. Backend calls the AI provider.
  5. Backend returns only the response your visitor needs.

An unsafe architecture looks like this:

// Do not do this in browser code.
fetch("https://api.example-ai-provider.com/v1/responses", {
  headers: {
    Authorization: "Bearer sk-real-key-here"
  }
});

If the key is in browser code, the question is not whether visitors can see it. They can. The only question is how quickly someone will find and misuse it.

3. Choose the right storage location for your host

There is no single storage method that fits every hosting stack. Use the strongest option your platform supports, and document where each secret lives.

Storage option Good use Main risk Practical check
Host or platform secret store Production app hosting, serverless functions, workers, CI/CD Misconfigured access or wrong environment scope Confirm the secret is available only to the production service that needs it
Environment variable Small server apps, VPS deployments, containers Debug tools, crash reports or process dumps may expose it Search logs for the variable value and avoid printing process.env
Cloud secret manager Multi-service apps, teams, regulated or high-risk systems Requires identity and access management discipline Grant read access only to the service identity that needs the secret
Protected config file Legacy PHP or WordPress setups File permissions, backups and accidental web exposure Keep it outside public web root where possible and restrict read permissions
CMS/plugin database field Plugin-only integrations Admin users, compromised plugins, exports and backups may reveal it Use only if the plugin has no safer method and the site risk is low

Heroku describes config vars as environment-specific configuration stored outside source code, exposed to app code as environment variables, and warns against direct references to sensitive variables where output may be written to logs. (devcenter.heroku.com) Cloudflare Workers has a separate concept of secrets for sensitive values such as API keys and auth tokens, and its docs also warn to add local .dev.vars* and .env* files to .gitignore. (developers.cloudflare.com) Vercel supports sensitive environment variables that are stored in a non-readable format once created. (vercel.com)

For larger or higher-risk projects, a cloud secret manager can add better access control, auditability and rotation workflows. Google Cloud’s Secret Manager best practices emphasize least privilege, separating staging and production environments, secret-level access controls, rotation and access logging; it also warns that secrets passed through files or environment variables can leak through directory traversal, debug endpoints or dependencies that log process details. (docs.cloud.google.com)

4. WordPress and CMS setups need extra caution

WordPress sites often add AI features through plugins. That is convenient, but it changes your threat model: the plugin, WordPress administrators, database backups, debug logs and other plugins may all become part of the secret’s exposure surface.

Use this order of preference:

  1. Best: plugin supports reading the key from an environment variable or a constant in wp-config.php.
  2. Acceptable for many small sites: plugin stores the key in its settings table, but access is restricted to trusted administrators and backups are protected.
  3. Avoid: key pasted into theme files, custom JavaScript, shortcode content, page builder blocks, analytics tags or frontend templates.

A WordPress AI plugin setup might look like this:

// wp-config.php, not a theme file.
define( 'MY_AI_PROVIDER_API_KEY', getenv( 'OPENAI_API_KEY' ) );

Then configure the plugin, custom integration or small must-use plugin to read MY_AI_PROVIDER_API_KEY on the server. If your host does not support environment variables, you may place the secret directly in wp-config.php, but protect the file carefully. WordPress hardening guidance says wp-config.php can be stored one directory above the WordPress installation and should be readable only by you and the web server, commonly permissions such as 400 or 440. (developer.wordpress.org)

Also disable unnecessary file editing in the WordPress dashboard. WordPress notes that administrators can edit PHP plugin and theme files by default, and that adding define( 'DISALLOW_FILE_EDIT', true ); to wp-config.php removes those editing capabilities from users. (developer.wordpress.org) This does not make a compromised administrator harmless, but it reduces one easy path from admin access to code execution and credential exposure.

Screenshot of a WordPress dashboard interface.
WordPress dashboard context for configuring plugins without exposing API keys in public code. — Gmoran6. Own work Source CC BY-SA 4.0

5. Backend route example: call the AI API without exposing the key

Here is the pattern for a small Node backend. The key is read from the server environment. The browser never receives it.

import express from "express";
import OpenAI from "openai";

const app = express();
app.use(express.json());

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY
});

app.post("/api/ai-summary", async (req, res) => {
  const input = String(req.body.text || "").slice(0, 4000);

  if (!input) {
    return res.status(400).json({ error: "Text is required." });
  }

  try {
    const response = await openai.responses.create({
      model: "gpt-4.1-mini",
      input: `Summarize this for a website editor:\n\n${input}`
    });

    res.json({ summary: response.output_text });
  } catch (error) {
    // Log a request ID or safe error code, not headers or environment variables.
    console.error("AI request failed", { message: error.message });
    res.status(502).json({ error: "AI request failed. Try again later." });
  }
});

The same principle works in Python: read the value with os.environ["OPENAI_API_KEY"], call the AI provider from the server, and return a sanitized response. Anthropic’s API key guidance similarly recommends environment variables and cloud secret management, and says local .env files should be ignored by source control; in cloud environments it recommends encrypted secret storage rather than dotenv files. (support.anthropic.com)

Before deploying this route, add:

  • Authentication or abuse controls if the endpoint can spend money.
  • Rate limits per user, session or IP address.
  • Request size limits.
  • Safe error messages.
  • A billing or usage alert at the AI provider if available.
  • Separate keys for development, staging and production.

6. Permissions, rotation and logging hygiene

Protecting secrets is not only about where they are stored. It is also about who can read them, what they can do and how quickly you can replace them.

Use least privilege. OWASP’s Secrets Management Cheat Sheet says engineers should not have access to all secrets and that secret management systems should support fine-grained access controls. (cheatsheetseries.owasp.org) For a website, that means:

  • Give the production app only the production AI key it needs.
  • Do not reuse the same key across all client sites.
  • Do not give a WordPress plugin a key with account-wide administrative privileges if a narrower key is available.
  • Use different secrets for local development, staging and production.
  • Remove access when a developer, agency, plugin or integration no longer needs it.

Logging needs the same discipline. OWASP’s logging guidance includes security-relevant events such as changes to privileges, assigning users to tokens and use or rotation of cryptographic keys as events worth logging. (cheatsheetseries.owasp.org) But logs should not contain the secret value itself. Avoid logging full request headers, full environment dumps, database connection strings, webhook payloads containing signatures, or complete exception objects from SDKs unless you have confirmed they are redacted.

Common logging mistakes:

  • Printing process.env or $_ENV during debugging.
  • Returning provider error details directly to the browser.
  • Leaving WordPress debug display enabled on production.
  • Logging Authorization headers in reverse proxies.
  • Sending raw webhook requests to third-party observability tools.

A useful check is to search your logs for the first and last six characters of each key. If you find a real key, rotate it.

7. What to do if a key leaks

Act as if the exposed key was copied. Do not wait to see abuse.

Leaked-key response checklist:

  1. Revoke or rotate the exposed key immediately. OWASP lists immediate revocation and fast rotation as core exposure responses. (cheatsheetseries.owasp.org)
  2. Create a replacement key with the minimum permissions needed.
  3. Update every place that used the old key: host secret store, CI/CD variables, WordPress config, worker secrets, background jobs and local .env files.
  4. Redeploy or restart services if your host requires it for new environment variables to take effect.
  5. Review provider usage, billing and access logs for unusual requests after the likely exposure time.
  6. Remove the secret from the exposed location: current code, old commits where practical, logs, support tickets, screenshots and documentation.
  7. Scan the repository and history. GitHub secret scanning scans Git history for hardcoded credentials and recommends rotating affected credentials immediately when an alert is received. (docs.github.com)
  8. Write down the cause: browser bundle, plugin setting, debug log, support ticket, copied .env, public repo, backup archive or mis-scoped platform variable.
  9. Add a prevention control: .gitignore, pre-commit secret scanning, safer plugin configuration, restricted logs, separate environments or a proper secret manager.

GitHub’s alert-resolution guidance also recommends reviewing and updating services that use the old token and checking security logs for unauthorized activity, depending on the provider. (docs.github.com)

Pre-launch secrets audit

Run this table before shipping an AI feature, chatbot, automation workflow or webhook integration.

Check Pass condition
Browser bundle No AI keys, database URLs, webhook secrets or private tokens in built JS, HTML source or source maps
Repository .env, .dev.vars, private keys and config files with real secrets are ignored and absent from Git history
Host settings Secrets are stored in platform secrets, sensitive env vars or a secret manager, not in public config
WordPress/CMS Plugin keys are not in theme files, page content, custom JS or screenshots
Permissions Production service can read only the secrets it needs
Environments Development, staging and production use separate keys
Logs Errors, headers, webhook payloads and environment dumps are redacted
Backups Database and file backups containing secrets are access-controlled
Rotation You know how to revoke, replace, deploy and verify each key
Abuse controls Public endpoints that spend AI credits have rate limits and safe error responses

Read next: How to Choose Hosting for an AI-Assisted Website Project