Skip to main content
Lesson 6 of 12 · xAI Security & Compliance

Protect Your API Key: Never Expose It on the Client Side

Lesson 6 of 12 Updated July 28, 2026 Advanced
On this page
  1. An API Key Is a Password
  2. Common Exposure Vectors
  3. Proxy Architecture: The Solution
  4. Secure Key Storage
  5. What to Do If Your Key Is Compromised
  6. Key Takeaways

An API Key Is a Password

Your xAI API key acts like a password with full access to your account. Anyone who has it can make API calls at your expense, access your models, and potentially compromise your data. How does it differ from a standard password? A stolen key can be used silently: you won’t see anything until the bill arrives, or until your finance team raises an alert. The rule is simple and absolute: never expose an API key on the client side.

Common Exposure Vectors

The most common case is direct inclusion of the key in frontend JavaScript:

// DANGER - NEVER do this
const response = await fetch("https://api.x.ai/v1/chat/completions", {
  headers: { "Authorization": "Bearer xai-abc123..." }
});

Once deployed, this code is visible to anyone using browser dev tools. You just need to open the Network tab to read the key in plain text in the request headers.

A more insidious vector, because it gives a false sense of security: frontend environment variables. Variables prefixed with NEXT_PUBLIC_, VITE_, or REACT_APP_ are bundled into the JavaScript at build time, and therefore public. The developer thinks they have “kept the key out of the code”; in reality, it is simply compiled into it:

# DANGER - this variable will be in the bundle
NEXT_PUBLIC_XAI_API_KEY=xai-abc123...

Git repositories are the third classic vector. Even a deleted commit remains in Git history: if you committed a key by mistake, rewriting history is not enough, because clones and forks may have retained it. Consider it compromised and revoke it. Finally, keys also leak through indirect paths, harder to imagine yet very common: application logs that trace full headers, error messages displayed to the user, or monitoring tools that capture outgoing requests.

Proxy Architecture: The Solution

The only secure approach is to proxy all calls through your backend:

Client (browser/mobile)
    ↓ request without API key
Your Backend (server)
    ↓ adds API key (environment variable)
xAI API
    ↓ response
Your Backend
    ↓ forwards response (filtered if needed)
Client

Here is a minimalist proxy in Python:

# Python Backend (Flask)
import os
from flask import Flask, request, jsonify
import requests

app = Flask(__name__)
XAI_KEY = os.environ["XAI_API_KEY"]  # Server-side variable only

@app.route("/api/chat", methods=["POST"])
def proxy_chat():
    user_message = request.json.get("message")

    response = requests.post(
        "https://api.x.ai/v1/chat/completions",
        headers={"Authorization": f"Bearer {XAI_KEY}"},
        json={
            "model": "grok-4.3",
            "messages": [{"role": "user", "content": user_message}]
        }
    )

    return jsonify(response.json())

Beyond protecting the key—which is never transmitted to the client—the proxy gives you a control point that direct calls make impossible. You can filter requests: rate limiting to prevent a client from draining your quota, input validation, content moderation. You control which models are accessible, preventing a hijacked client from calling an expensive model and generating unexpected costs. You can log calls for auditing—a central need for a compliance lead. And you can cache certain responses to reduce costs on repetitive requests.

Secure Key Storage

On the server side, the baseline is the environment variable, loaded from a .env file that is never committed to Git:

# .env file (NEVER committed to Git)
XAI_API_KEY=xai-your-secret-key

Systematically add these files to your .gitignore:

# .gitignore
.env
.env.local
.env.production

For production environments, a .env file placed on a server remains a fragile manual process: prefer a dedicated secrets manager. Proven options include AWS Secrets Manager or SSM Parameter Store, Google Secret Manager, Azure Key Vault, HashiCorp Vault, or Doppler and Infisical for lighter structures. What these tools provide over a flat file: automatic key rotation, access auditing—who read which secret, when—and encryption at rest. These are elements you can present as-is during a security audit, whereas a text file on a machine proves nothing.

What to Do If Your Key Is Compromised

If you suspect a key has been exposed, the order of operations matters as much as the operations themselves. Revoke the key in the xAI console (console.x.ai) before anything else: every minute the compromised key remains active is a minute of possible exploitation, and service continuity is handled after, not before. Then generate a new key and update your backends to restore service.

The incident is not closed yet. Audit usage by looking for suspicious API calls in your billing history—a spike during an unusual time window, usage attributed to a model you do not use. Identify the source of the leak, otherwise the new key will follow the same path as the old one. Finish by scanning your code with tools like gitleaks or truffleHog, which also traverse Git history and often reveal other secrets forgotten long ago.

Key Takeaways

  • An API key is a password with full access to your account
  • Always proxy API calls through your backend
  • Variables NEXT_PUBLIC_, VITE_, REACT_APP_ are public
  • Use a secrets manager in production
  • If a key is compromised, revoke it immediately and audit usage