Skip to content

Security Notes

  • Keys are shown once at creation. Copy and store them immediately in a secure location.
  • Never commit keys to version control. Use environment variables or a secrets manager.
  • Never include keys in client-side code, browser bundles, or frontend JavaScript.
  • Rotate a key immediately if it appears in logs, screenshots, chat, or any unsecured location.

Use the placeholder sk-bve-YOUR_KEY in documentation and examples — never a real key.

The admin key (ADMIN_API_KEY) grants full control over all API keys and usage data.

  • Set it as a Cloudflare Worker secret, not a plain environment variable:

    Terminal window
    bunx wrangler secret put ADMIN_API_KEY
  • Grant access only to trusted operators.

  • Rotate via bunx wrangler secret put ADMIN_API_KEY — this redeploys the Worker with the new value.

Use the placeholder admin_bve_YOUR_ADMIN_KEY in documentation and examples.

BVE Gateway never stores API keys in plain text. Only sha256(key + pepper) is stored in Cloudflare D1. The pepper is a separate API_KEY_PEPPER Worker secret.

This means:

  • A database breach does not expose raw keys.
  • A lost key cannot be recovered — revoke it and create a new one.

BVE Gateway allows cross-origin requests from any origin (*). Allowed request headers:

  • Authorization
  • Content-Type
  • X-Request-Id
  • OpenAI-Beta — listed so browser preflight passes; stripped before upstream forwarding (see Upstream header sanitization)
  • Anthropic-Version — required by browser clients calling /v1/messages
  • Anthropic-Beta — required by browser clients using Anthropic beta features (e.g. tools-2024-04-04)

Exposed response headers (accessible to browser JavaScript):

HeaderDescription
X-Request-IdGateway per-request UUID
X-BVE-Client-IdEcho of the client-supplied X-Request-Id (when present and valid: alphanumeric + -_., ≤ 128 chars). Absent when the client did not send X-Request-Id or the value failed validation. Lets clients confirm their trace ID was received without querying the log stream.
X-BVE-LatencyTotal gateway latency in milliseconds
X-BVE-ModelModel ID the gateway processed this request with
X-BVE-CacheModel-list cache status: HIT, STALE, or MISS
X-BVE-WorkerWorker version UUID — lets operators identify the exact deployed version from curl or browser dev tools without reading log streams
X-Quota-Allowed, X-Quota-Available, X-Quota-ResetFuelix upstream quota headers
X-RateLimit-Limit-Requests, X-RateLimit-Limit-TokensUpstream request/token limits
X-RateLimit-Remaining-Requests, X-RateLimit-Remaining-TokensUpstream remaining capacity
X-RateLimit-Reset-Requests, X-RateLimit-Reset-TokensUpstream window reset times
X-RateLimit-Limit-DayBVE Gateway per-day request cap for this key
X-RateLimit-Remaining-DayRequests remaining until UTC midnight
X-RateLimit-Reset-DaySeconds until the next UTC midnight
X-RateLimit-Limit-MonthBVE Gateway per-month request cap for this key (when configured)
X-RateLimit-Remaining-MonthRequests remaining in the current calendar month
X-RateLimit-Reset-MonthSeconds until the start of the next calendar month
Retry-AfterSeconds until the rate-limit window resets
Anthropic-RateLimit-Requests-Limit, Anthropic-RateLimit-Requests-Remaining, Anthropic-RateLimit-Requests-ResetAnthropic Messages API request limits
Anthropic-RateLimit-Tokens-Limit, Anthropic-RateLimit-Tokens-Remaining, Anthropic-RateLimit-Tokens-ResetAnthropic Messages API combined token limits
Anthropic-RateLimit-Input-Tokens-Limit, Anthropic-RateLimit-Input-Tokens-Remaining, Anthropic-RateLimit-Input-Tokens-ResetAnthropic per-direction input-token limits
Anthropic-RateLimit-Output-Tokens-Limit, Anthropic-RateLimit-Output-Tokens-Remaining, Anthropic-RateLimit-Output-Tokens-ResetAnthropic per-direction output-token limits
X-Groq-Request-IdGroq request ID for log correlation and support tickets
X-Groq-Processing-TimeGroq server-side inference duration in seconds (e.g. 0.4823)
OpenAI-Processing-MsOpenAI server-side processing time in milliseconds
X-Cohere-Request-IdCohere request correlation ID for support tickets
X-OpenRouter-ModelActual model ID selected by OpenRouter after provider routing (e.g. openai/gpt-4o)
X-Or-Cache-StatusOpenRouter semantic cache result: HIT or MISS
X-Or-Remaining-TokensToken budget remaining in the OpenRouter rate-limit window

Preflight responses are cached for 24 hours.

If your use case requires origin restrictions, this must be changed at the Worker level.

Every response from the gateway includes the following security headers:

HeaderValuePurpose
X-Content-Type-OptionsnosniffPrevents browsers from MIME-sniffing responses
Referrer-Policyno-referrerPrevents the Referer header from leaking to upstream
Content-Security-Policydefault-src 'none'Prevents browsers from executing gateway responses as HTML or scripts
Permissions-Policyinterest-cohort=()Opts the gateway out of Privacy Sandbox FLoC cohort calculation
Cache-Controlno-storePrevents authenticated responses from being cached by intermediary proxies
Strict-Transport-Securitymax-age=63072000Instructs browsers to use HTTPS only for 2 years; prevents HTTP downgrade attacks
X-Permitted-Cross-Domain-PoliciesnonePrevents Adobe Flash, Adobe Reader, and similar plugin runtimes from making cross-domain policy requests to this API server

proxyToFuelix strips or rewrites the following client-supplied headers before forwarding to Fuelix, preventing credential injection, IP spoofing, and information leakage.

Credential and auth headers (stripped — prevent alternate auth injection and billing context manipulation):

  • x-api-key — Anthropic alternate auth header
  • api-key — Azure OpenAI SDK credential header (no x- prefix; sending an Azure key here would expose it to an upstream provider)
  • openai-organization, openai-project — OpenAI billing context
  • openai-beta — OpenAI Assistants API feature flag (OpenAI-Beta: assistants=v2); forwarding this could enable experimental Fuelix features that BVE Gateway does not expose. Accepted by CORS preflight but stripped before upstream forwarding.
  • x-basicllm-* — Fuelix internal routing headers
  • x-goog-api-key — Google Gemini/VertexAI API key
  • anthropic-auth-token, x-anthropic-auth-token — Older Anthropic auth variants
  • anthropic-dangerous-direct-browser-only — Anthropic header that bypasses CORS for direct browser-to-Anthropic calls; if forwarded, would route the request under direct Anthropic billing rather than through Fuelix

Session and proxy credentials (stripped — must never leave the gateway):

  • cookie — Browser session cookies have no meaning to Fuelix and must not leave the gateway
  • proxy-authorization — Proxy credentials from intermediate HTTP proxies must not reach the upstream provider
  • host — Stripped so the Workers runtime derives the correct Host from the upstream URL; a client-supplied Host could affect virtual-host routing in Fuelix

Cloudflare metadata headers (stripped — prevent leaking Cloudflare-internal routing context to upstream):

  • CF-Connecting-IP — Cloudflare verified client IP (read by the gateway, then stripped)
  • CF-Ray — Cloudflare internal request trace ID
  • CF-Visitor — Cloudflare scheme/port metadata
  • CF-IPCountry — Cloudflare GeoIP result
  • True-Client-IP — Cloudflare Enterprise real-IP header

Forwarding and IP headers (sanitized or stripped — prevent host/IP spoofing in Fuelix logs and routing):

  • X-Forwarded-Foroverwritten (not stripped): replaced with the Cloudflare-verified client IP from CF-Connecting-IP. A client-supplied X-Forwarded-For would otherwise let an attacker spoof their apparent source IP in Fuelix’s access logs and abuse-detection systems.
  • x-forwarded-host — Stripped: allows callers to inject a fake host; frameworks may use this to build absolute redirect URLs (e.g. Location: https://evil.com/callback).
  • x-forwarded-proto — Stripped: the upstream connection is always HTTPS; a client-supplied value could mislead Fuelix into treating the connection as plain HTTP.
  • x-forwarded-scheme — Stripped: scheme alias used by Nginx and Kubernetes Ingress controllers; same spoofing risk as x-forwarded-proto.
  • x-forwarded-ssl — Stripped: boolean SSL-flag header used by some proxies (e.g. HAProxy); a client-supplied off value could mislead upstream services that use it to detect HTTPS.
  • x-forwarded-server — Stripped: server-identity metadata set by some reverse proxies (e.g. Apache); could expose or spoof internal server hostnames in Fuelix’s logs.
  • x-forwarded-port — Stripped: port metadata used by some proxies; a client-supplied value could mislead URL construction in frameworks that build absolute redirect targets.
  • x-forwarded-prefix — Stripped: URL-prefix header used by Spring Boot and similar frameworks; could affect path rewriting or routing in the upstream application layer.
  • x-real-ip — Stripped: redundant with X-Forwarded-For (which is overwritten from CF-Connecting-IP); a client-supplied value would spoof their IP in Fuelix’s logs.
  • forwarded — Stripped: RFC 7239 structured equivalent of X-Forwarded-*; carries the same spoofing risk.

Browser request metadata headers (stripped by prefix — irrelevant to upstream and leak client context):

  • sec-fetch-site, sec-fetch-mode, sec-fetch-dest, sec-fetch-user — Browser security metadata; no meaning to an API upstream.
  • sec-purpose, sec-gpc — Additional browser security/privacy metadata.
  • All sec-* prefixed headers are stripped in a single pass.

SDK telemetry headers (stripped by prefix — prevent leaking client environment metadata to upstream):

  • x-stainless-lang, x-stainless-version, x-stainless-os, x-stainless-runtime, x-stainless-runtime-version, x-stainless-arch, x-stainless-async — Emitted by Anthropic, OpenAI, and other Stainless-generated SDK clients; they expose client OS, language, SDK version, and runtime to the upstream provider.
  • All x-stainless-* prefixed headers are stripped in a single pass.

Gateway-internal headers (stripped by prefix — gateway annotations must not appear on upstream requests):

  • X-BVE-Worker, X-BVE-Model, X-BVE-Latency, X-BVE-Cache, X-BVE-Signature — BVE Gateway metadata set only on responses. A client injecting any x-bve-* header into their request would have it forwarded to Fuelix, breaking the protocol separation between gateway-internal annotations and upstream requests.
  • All x-bve-* prefixed headers are stripped in a single pass.

Only a safe subset of upstream headers from Fuelix is forwarded to clients. All other upstream headers (e.g. cf-*, x-cloud-trace-context, set-cookie, via, x-powered-by, and all x-basicllm-*) are stripped. Forwarded headers:

HeaderSourceNotes
content-typeFuelix upstream
content-lengthFuelix upstream
content-dispositionFuelix upstreamPresent on file download responses
cache-controlFuelix upstream
x-request-idFuelix upstreamFuelix’s own request ID
request-idFuelix upstreamAnthropic Messages API uses this header name
x-quota-allowedFuelix upstream
x-quota-availableFuelix upstream
x-quota-resetFuelix upstream
x-ratelimit-limit-requestsFuelix upstreamReflects Fuelix upstream account limits
x-ratelimit-limit-tokensFuelix upstreamReflects Fuelix upstream account limits
x-ratelimit-remaining-requestsFuelix upstreamReflects Fuelix upstream account limits
x-ratelimit-remaining-tokensFuelix upstreamReflects Fuelix upstream account limits
x-ratelimit-reset-requestsFuelix upstreamReflects Fuelix upstream account limits
x-ratelimit-reset-tokensFuelix upstreamReflects Fuelix upstream account limits
retry-afterFuelix upstreamForwarded from upstream 429/503 responses
anthropic-ratelimit-requests-limitFuelix upstreamAnthropic Messages API
anthropic-ratelimit-requests-remainingFuelix upstreamAnthropic Messages API
anthropic-ratelimit-requests-resetFuelix upstreamAnthropic Messages API
anthropic-ratelimit-tokens-limitFuelix upstreamAnthropic Messages API
anthropic-ratelimit-tokens-remainingFuelix upstreamAnthropic Messages API
anthropic-ratelimit-tokens-resetFuelix upstreamAnthropic Messages API
x-groq-request-idFuelix upstreamGroq request ID for log correlation and support tickets
x-groq-processing-timeFuelix upstreamGroq server-side inference duration in seconds (e.g. 0.4823); validated and capped at 300 s
openai-processing-msFuelix upstreamOpenAI server-side processing time in milliseconds
x-cohere-request-idFuelix upstreamCohere request correlation ID for support tickets
x-openrouter-modelFuelix upstreamActual model ID selected by OpenRouter after provider routing
x-or-cache-statusFuelix upstreamOpenRouter semantic cache result: HIT or MISS
x-or-remaining-tokensFuelix upstreamToken budget remaining in the OpenRouter rate-limit window
X-Request-IdAdded by gatewayPer-request UUID (distinct from upstream x-request-id)
X-BVE-LatencyAdded by gatewayTotal gateway latency in milliseconds
X-BVE-ModelAdded by gatewayModel ID processed by the gateway (from request body or response JSON)
X-BVE-CacheAdded by gatewayModel-list cache status for GET /v1/models: HIT, STALE, or MISS
X-BVE-WorkerAdded by gatewayDeployed Worker version UUID — lets operators identify the exact version from curl or browser dev tools (production only; absent in local dev)

The gateway’s structured request logger applies redactSecrets() to several user-controlled fields before emitting each log line. This prevents accidental credential leakage in wrangler tail and Logpush records.

Log fieldRedaction appliedWhy
modelProvider key patterns replaced with [REDACTED]A malicious client could set the model field to a key-like string (e.g. sk-proj-...). The field is validated and blocked, but the raw value is still set in context before the 403 is returned, and would appear in the log without redaction.
ua (User-Agent)Provider key patterns replaced with [REDACTED]Some SDK versions and custom HTTP clients embed API key material in their User-Agent string (e.g. my-client/1.0 (api-key=sk-bve-...)). Without redaction these keys appear verbatim in every log record.
queryCredential-like query param values replaced with [REDACTED]A misconfigured SDK may pass auth via query string (?api_key=sk-bve-...). The gateway strips these before forwarding to Fuelix, but without log redaction the raw value would appear in the structured log. Redacted params: api_key, api-key, apikey, access_token, secret, token, key, password.

redactSecrets() matches the following key prefixes and replaces matched substrings with [REDACTED]:

PrefixProvider
sk-OpenAI / BVE Gateway API keys
sk-ant-Anthropic
sk-proj-OpenAI project keys
fuelix-Fuelix upstream keys
gsk_Groq
r8_Replicate
AIzaSyGoogle AI Studio / Vertex AI
hf_Hugging Face
nvapi-NVIDIA NIM / NGC API keys
xai-xAI Grok
co_Cohere (v2 API keys; appears in embedding and chat tool-call error messages)
tvly-Tavily AI (web search; appears in agent/RAG tool-call flows)
pcsk_Pinecone serverless (vector DB; appears in RAG tool-call flows)
wcs_Weaviate Cloud Service (vector DB; appears in RAG/hybrid-search retrieval flows)
pa-Voyage AI (embedding API keys; format: pa- + 32+ chars; appears in RAG retrieval flows)
sk_ElevenLabs (underscore variant: sk_ + 8+ alphanum; distinct from OpenAI’s sk- hyphen form)
pplx-Perplexity AI (appears in agent/RAG pipelines that use Perplexity for web-grounded generation)
fw_Fireworks AI (fw_ + 20+ alphanum; fast-inference provider for Llama/Mixtral; appears in agent pipelines)
AKIAAWS IAM access key IDs (AKIA + exactly 16 uppercase alphanum; surface when AWS-integrated services reject auth)
dapiDatabricks personal access tokens (dapi + 32 alphanum; Databricks is a common LLM/embedding backend)
ghp_, gho_, ghs_, ghu_GitHub classic tokens — personal (ghp_), OAuth (gho_), server-to-server (ghs_), user-to-server (ghu_); always 40 chars total
github_pat_GitHub fine-grained personal access tokens (introduced 2022; 20+ chars after the 11-char prefix)
glpat-GitLab personal access tokens (glpat- + 20+ alphanum/hyphen; common in CI/CD pipelines)
csk-Cerebras Cloud API keys (csk- + 8+ alphanum/hyphen; fast-inference provider for large open-weight models)

Non-JSON upstream 4xx response bodies (plain text, HTML error pages from upstream CDN/auth layers) also have redactSecrets() applied before being forwarded to the API client.

All sensitive values must be set as Cloudflare Worker secrets (not plain vars):

SecretPurposeSet with
FUELIX_API_KEYUpstream Fuelix API keybunx wrangler secret put FUELIX_API_KEY
ADMIN_API_KEYAdmin API authenticationbunx wrangler secret put ADMIN_API_KEY
API_KEY_PEPPERKey hash pepperbunx wrangler secret put API_KEY_PEPPER
WEBHOOK_URLAlert webhook endpoint (optional)bunx wrangler secret put WEBHOOK_URL
WEBHOOK_SECRETHMAC signing key for webhook requests (optional)bunx wrangler secret put WEBHOOK_SECRET

The WEBHOOK_URL secret is optional. When set, the queue consumer POSTs a structured JSON notification to this URL for every alert event (quota warnings, expiry alerts, cron failures) and every audit event (key lifecycle changes). Leave unset to disable webhook notifications.

The WEBHOOK_SECRET secret is optional. When set alongside WEBHOOK_URL, every webhook POST includes an X-BVE-Signature: sha256=<hmac-sha256-hex> header so your receiver can verify authenticity. See Deployment Notes for the verification pattern.

Rotate the first three if the Worker is compromised. Secrets are not visible in Wrangler config or logs after being set.

Revocation is permanent and immediate. A revoked key:

  • Returns 403 api_key_revoked on all subsequent requests.
  • Cannot be restored — create a new key if access is needed again.

To revoke:

Terminal window
curl -X POST https://api.bve.me/admin/api-keys/KEY_UUID/revoke \
-H "Authorization: Bearer admin_bve_YOUR_ADMIN_KEY"