# Cloudflare

- Canonical URL: https://docs.fairvisor.com/docs/gateway/cloudflare/
- Section: docs
- Last updated: n/a
> Integrating Fairvisor Edge with Cloudflare via Workers decision checks or reverse-proxy origin pattern.


Cloudflare integration is typically implemented with a Worker that calls Fairvisor before forwarding traffic to your origin.

## Integration patterns

| Pattern | When to use |
|---|---|
| **Worker decision check** (recommended) | You need strict control over allow/reject handling and header propagation |
| **Fairvisor as origin proxy** | You want Cloudflare to route directly to Fairvisor in `reverse_proxy` mode |

## Pattern 1 — Worker decision check (recommended)

Flow:

1. Worker receives request
2. Worker calls `POST /v1/decision`
3. `200`: forward to origin
4. `429`: return reject with `Retry-After`, `X-Fairvisor-Reason`, and `RateLimit*`

```js
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const originalUri = url.pathname + url.search;

    const decisionHeaders = new Headers();
    decisionHeaders.set("X-Original-Method", request.method);
    decisionHeaders.set("X-Original-URI", originalUri);

    const auth = request.headers.get("authorization");
    if (auth) decisionHeaders.set("Authorization", auth);

    const clientIp = request.headers.get("cf-connecting-ip");
    if (clientIp) decisionHeaders.set("X-Forwarded-For", clientIp);

    const decisionRes = await fetch(`${env.FAIRVISOR_EDGE_URL}/v1/decision`, {
      method: "POST",
      headers: decisionHeaders,
    });

    if (decisionRes.status === 429) {
      const headers = new Headers();
      for (const k of [
        "retry-after",
        "x-fairvisor-reason",
        "ratelimit",
        "ratelimit-limit",
        "ratelimit-remaining",
        "ratelimit-reset",
      ]) {
        const v = decisionRes.headers.get(k);
        if (v) headers.set(k, v);
      }
      return new Response("Too Many Requests", { status: 429, headers });
    }

    if (!decisionRes.ok) {
      // Fail policy choice:
      // - fail-closed: return 503
      // - fail-open: continue to origin
      return new Response("Decision service unavailable", { status: 503 });
    }

    const upstream = await fetch(new Request(request, { cf: { cacheEverything: false } }));
    return upstream;
  }
};
```

### Required env vars

```text
FAIRVISOR_EDGE_URL=https://fairvisor-edge.internal.example.com
```

Keep Fairvisor reachable only from trusted network paths (for example private origin network or tunnel), not public internet.

## Pattern 2 — Fairvisor as reverse-proxy origin

Set Cloudflare origin to Fairvisor Edge, run edge in `reverse_proxy` mode:

```bash
FAIRVISOR_MODE=reverse_proxy
FAIRVISOR_BACKEND_URL=http://your-origin-service:3000
FAIRVISOR_CONFIG_FILE=/etc/fairvisor/policy.json
```

In this setup, every request to origin is enforced inline by Fairvisor.

## Failure policy

Cloudflare pattern should explicitly choose fail-open or fail-closed:

- **Fail-closed**: safer for sensitive API routes
- **Fail-open**: better availability for low-risk/public routes

Recommended split is documented in [Gateway Failure Policy](/docs/reference/gateway-failure-policy/).

## Timeout and retry guidance

- Keep decision timeout low (typically 200-500 ms budget)
- Avoid repeated retries on the hot path
- Emit logs/metrics for every fallback decision

## Header mapping checklist

When calling Fairvisor, include:

- `X-Original-Method`
- `X-Original-URI`
- `Authorization` (if used for JWT descriptors)
- `X-Forwarded-For` (from `cf-connecting-ip`)

## Verification

```bash
# readiness
curl -i http://fairvisor-edge.internal.example.com:8080/readyz

# direct decision probe
curl -i -X POST http://fairvisor-edge.internal.example.com:8080/v1/decision \
  -H "X-Original-Method: GET" \
  -H "X-Original-URI: /v1/chat/completions" \
  -H "X-Forwarded-For: 198.51.100.42"
```

Look for `429` rejects with `X-Fairvisor-Reason` and `Retry-After`.

## Troubleshooting

| Symptom | Likely cause | Fix |
|---|---|---|
| Worker always returns 503 | Fairvisor unreachable or timeout too strict | Verify network path and adjust timeout budget |
| Fairvisor sees wrong route | Missing or malformed `X-Original-URI` | Forward full path + query |
| Per-user limits collapse into one bucket | Missing identity headers/JWT | Forward `Authorization` and required descriptor headers |

See [Decision API](/docs/reference/api/) for the full contract.

