{
  "id": "build-a-cloudflare-worker-api-proxy",
  "type": "prompts",
  "category": "prompts",
  "locale": "es",
  "url": "/es/prompts/build-a-cloudflare-worker-api-proxy",
  "title": "Indicación para construir un proxy de API de Cloudflare Worker",
  "description": "Indicación de IA para copiar y pegar con el fin de construir un Worker de Cloudflare que haga de proxy, limite la tasa de llamadas API externas, añada encabezados de autenticación y almacene en caché las respuestas.",
  "tools": [
    "Cursor",
    "Claude Code",
    "Codex",
    "Windsurf"
  ],
  "stack": [
    "Cloudflare",
    "TypeScript"
  ],
  "tags": [
    "cloudflare",
    "typescript",
    "security",
    "deploy"
  ],
  "difficulty": "medium",
  "updated": "2026-06-08",
  "markdown": "Utiliza este prompt para construir un Cloudflare Worker que se sitúe delante de una API externa,\ninyecte cabeceras de autenticación, limite la tasa por IP usando Workers KV y almacene en caché las respuestas —\npara que los clientes nunca vean tu clave de API upstream.\n\n## Prompt Principal\n\n```txt title=\"Main Prompt\"\nYou are building a Cloudflare Worker using TypeScript and the Workers runtime (not Node.js).\nThe Worker will proxy requests to an upstream API (e.g., OpenAI at https://api.openai.com).\n\nTask: create a production-ready API proxy Worker.\n\nRequirements:\n- Use `wrangler` v3 for local dev. Scaffold with `bun create cloudflare@latest` and choose\n  \"Hello World\" Worker with TypeScript.\n- Upstream URL: read from a Wrangler secret `UPSTREAM_URL` (string).\n- Auth: inject `Authorization: Bearer ${env.UPSTREAM_API_KEY}` on every proxied request,\n  where `UPSTREAM_API_KEY` is a Wrangler secret. Never expose this header to the client.\n- CORS: allow only origins in `env.ALLOWED_ORIGINS` (comma-separated string secret).\n  Return a `403` for disallowed origins. Handle preflight OPTIONS requests.\n- Rate limiting: use Workers KV binding `RATE_LIMIT_KV`.\n  - Key: `rl:${ip}` where ip is `request.headers.get('CF-Connecting-IP')`.\n  - Value: request count for the current UTC minute (TTL = 60 s).\n  - Limit: 60 requests/minute per IP. Return `429` with `Retry-After: 60` if exceeded.\n- Caching: for GET requests, check `caches.default` before proxying upstream. Cache\n  successful responses with `Cache-Control: public, max-age=300`.\n- Strip the following headers from the upstream response before returning to the client:\n  `x-powered-by`, `server`, `cf-ray`.\n- Wrangler config: declare the KV namespace binding and all secrets in `wrangler.toml`.\n- Do NOT use Node.js APIs (`fs`, `path`, `Buffer`) — Workers runtime only.\n\nStop and list all planned files before writing code.\n```\n\n## Notas de Implementación\n\n- Los Cloudflare Workers reciben un `Request` y devuelven un `Response` — evita patrones al estilo `express`.\n- `caches.default` es la caché de borde de Cloudflare; solo funciona en producción. Usa `MINIFLARE_CACHE`\n  para pruebas locales o simula la caché.\n- `CF-Connecting-IP` es inyectado por Cloudflare — no es suplantable desde Internet público, pero\n  prueba localmente con una IP de respaldo fija.\n- Los secretos de Wrangler se configuran con `wrangler secret put UPSTREAM_API_KEY` — nunca los almacenes en\n  `wrangler.toml` o en archivos `.env` confirmados.\n\n## Cambios Esperados en Archivos\n\n```txt\nwrangler.toml                  (new)\nsrc/index.ts                   (new — Worker entrypoint)\nsrc/cors.ts                    (new — CORS helper)\nsrc/rate-limit.ts              (new — KV rate limiter)\npackage.json                   (new)\ntsconfig.json                  (new)\n.dev.vars                      (new — local dev secrets, gitignored)\n.gitignore                     (edited — add .dev.vars)\n```\n\n## Criterios de Aceptación\n\n- `wrangler dev` se inicia sin errores y redirige un `GET /` a la URL upstream.\n- Una IP que envía 61 solicitudes en un minuto recibe un `429` en la 61.ª solicitud.\n- Una solicitud desde un origen no permitido recibe un `403`.\n- La cabecera `Authorization` no aparece en la respuesta ni en ninguna cabecera visible para el cliente.\n- `wrangler deploy` se ejecuta correctamente y el Worker está activo en `workers.dev`.\n\n## Comandos de Prueba\n\n```bash\nwrangler dev &\n# test normal proxy\ncurl http://localhost:8787/ -H \"Origin: https://myapp.com\"\n# test CORS rejection\ncurl http://localhost:8787/ -H \"Origin: https://evil.com\"\n# test rate limit (requires 61 rapid requests)\nfor i in $(seq 1 62); do curl -s -o /dev/null -w \"%{http_code}\\n\" http://localhost:8787/; done\n```\n\n## Errores Comunes de la IA\n\n- Usar `process.env` en lugar del parámetro `env` pasado al manejador `fetch` del Worker.\n- Olvidar manejar las solicitudes de verificación `OPTIONS`, rompiendo CORS para llamadas POST/PUT.\n- Almacenar `UPSTREAM_API_KEY` en `wrangler.toml` como una variable simple en lugar de un secreto.\n- Usar `node:buffer` u otros módulos integrados de Node.js, que no están disponibles en el runtime de Workers.\n\n## Prompt de Corrección\n\n```txt title=\"Fix Prompt\"\nThe Worker fails with a runtime error or leaks the API key. Fix in order:\n1. Replace `process.env.X` with `env.X` everywhere — Workers use the `env` handler parameter.\n2. Add an OPTIONS handler before the proxy logic that returns the CORS headers with a 204 status.\n3. Move `UPSTREAM_API_KEY` from `wrangler.toml` [vars] to a secret: `wrangler secret put UPSTREAM_API_KEY`.\nShow only the corrected diff.\n```"
}