shiply.now

Proxy routes

Call AI APIs and databases from a static site — secrets stay server-side

Proxy routes let your published site make authenticated calls to external APIs — OpenAI, OpenRouter, Supabase, anything — without ever shipping the API key to the browser. You declare routes in a manifest; shiply injects the secrets server-side at request time.

How it works

  1. Store your key in Variables (encrypted at rest).
  2. Publish a .shiply/proxy.json manifest with your site's files.
  3. Your page calls a relative URL — shiply matches it, adds the headers with ${VAR} resolved from your Variables, and forwards to the upstream.

The manifest itself is never served to visitors, and Set-Cookie from upstreams is stripped.

Manifest: .shiply/proxy.json

{
  "proxies": {
    "/api/chat": {
      "upstream": "https://openrouter.ai/api/v1/chat/completions",
      "method": "POST",
      "headers": { "Authorization": "Bearer ${OPENROUTER_API_KEY}" }
    },
    "/api/db/*": {
      "upstream": "https://xyz.supabase.co/rest/v1",
      "headers": { "apikey": "${SUPABASE_ANON_KEY}" }
    }
  }
}
  • Exact routes (/api/chat) match only that path. An optional method restricts the HTTP verb.
  • Prefix routes (/api/db/*) match the prefix and append the remainder (plus query string) to the upstream: /api/db/users?limit=1https://xyz.supabase.co/rest/v1/users?limit=1.
  • ${VAR_NAME} tokens in header values resolve from your account Variables at request time. A missing variable returns 502 {"error":"proxy_var_missing"} naming the variable.

Calling it from your page

const res = await fetch('/api/chat', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    model: 'anthropic/claude-sonnet-4-6',
    messages: [{ role: 'user', content: 'hello' }],
  }),
})
const data = await res.json()

Streaming responses (SSE) pass through, so chat UIs can stream tokens.

Rules & limits

RuleValue
Site must be ownedanonymous sites get 403 proxy_requires_account — claim first
Upstreamshttps:// only; public hosts only (no localhost/private IPs)
Routes per site20
Request body10 MB max
Rate limit100 requests/hour per IP per route
Visitor cookiesnot forwarded upstream; upstream Set-Cookie stripped

Errors

StatusBody errorFix
403proxy_requires_accountclaim the site / publish with an API key
502proxy_var_missingadd the named variable in Dashboard → Variables
502proxy_upstream_unreachablecheck the upstream URL
429rate_limitedback off; raise limits on a paid plan (contact support)
400proxy_manifest_invalidfix .shiply/proxy.json and re-publish