Cloudflare AI Gateway is a proxy for AI requests. It adds request logs, cost analytics, caching and rate limits in the Cloudflare dashboard. You create and configure a gateway in the dashboard. The package only sends requests to it.
To compare the gateway route with the direct route, see Direct and gateway routes.
Route requests through a gateway
Create a gateway
In the Cloudflare dashboard, go to AI, then AI Gateway, then Create Gateway. Copy the gateway name. This name is the slug.
Add the slug to your .env file
Add this line to your application’s
.envfile:CLOUDFLARE_AI_GATEWAY=my-gatewayRead it in config/ai.php
Add the
gatewayline to the provider entry. It reads the value from.env, so do not write the slug here.'workers-ai' => [ 'driver' => 'workers-ai', 'key' => env('CLOUDFLARE_AI_API_TOKEN'), 'account_id' => env('CLOUDFLARE_ACCOUNT_ID'), 'gateway' => env('CLOUDFLARE_AI_GATEWAY'), ],Send a request and open the logs
Your calling code does not change. Open the gateway in the dashboard and go to Logs. Text and embedding calls show under
compat.
Text and embedding calls go to https://gateway.ai.cloudflare.com/v1/<account_id>/<gateway>/compat. Clef and Clairic calls go to api.cloudflare.com with a cf-aig-gateway-id header, and Cloudflare applies the gateway’s features to them.
Use only the slug. If you give a URL or a value with a slash, the package throws and tells you to use the url key.
Authenticated Gateway
With Authenticated Gateway on, gateway.ai.cloudflare.com rejects a request that has no gateway token. To set it up:
- Open the gateway settings in the dashboard.
- Create an authentication token. This is the gateway token, and it has Run permission. Copy it.
- Turn on Authenticated Gateway.
- Add the token to your application’s
.envfile:
CLOUDFLARE_AI_GATEWAY_TOKEN=your-gateway-token- Read it in the provider entry in
config/ai.php:
'workers-ai' => [
// ...
'gateway_token' => env('CLOUDFLARE_AI_GATEWAY_TOKEN'),
],The package sends cf-aig-authorization: Bearer <gateway_token>. Your key still goes in Authorization. If gateway_token is empty, the package does not send the header.
Calls to api.cloudflare.com, such as Clef and Clairic, do not need the gateway token. That API reads your API token from Authorization. Tested on 2026-11-05. FLUX.2 image models do need it. See Non-JSON models.
Without the header, or with a wrong token, an authenticated gateway returns HTTP 401 with {"code":2009,"message":"Unauthorized"}. Laravel throws Illuminate\Http\Client\RequestException. The message does not name the gateway or the header, so check gateway_token first.
Direct route with a gateway header
Cloudflare’s REST API on api.cloudflare.com also routes through a gateway. Send the cf-aig-gateway-id header on the direct route. It needs no gateway key, and no second token even on an authenticated gateway. FLUX.2 is the exception, and nova-3 cannot use a gateway. See Non-JSON models.
Keep the slug in your application’s .env file, as CLOUDFLARE_AI_GATEWAY, and read it in config/ai.php:
'workers-ai' => [
'driver' => 'workers-ai',
'key' => env('CLOUDFLARE_AI_API_TOKEN'),
'account_id' => env('CLOUDFLARE_ACCOUNT_ID'),
'headers' => ['cf-aig-gateway-id' => env('CLOUDFLARE_AI_GATEWAY')],
],Text, Clef, Clairic and embeddings all returned HTTP 200 this way on 2026-11-05. Cloudflare’s gateway docs list chat completions on this API, but not embeddings. See the AI Gateway REST API.
Third-party models
Cloudflare’s REST API serves other providers’ models with your Cloudflare token. Use the direct route, and name the model in author/model form:
use function Laravel\Ai\agent;
agent()->prompt('Hello', provider: 'workers-ai', model: 'openai/gpt-5.5');- Cloudflare bills the call from AI Gateway credits, or uses a key stored under the
defaultalias. See Unified Billing and BYOK. - The call goes through your account’s default gateway. To pick another, add the
cf-aig-gateway-idheader as in the previous section. - The token needs Account, Workers AI, Read, the same as other calls. Tested on 2026-11-05 with
openai/gpt-4.1-mini.
Use another provider’s driver through a gateway
You can point another Laravel AI driver at a gateway to get logs and caching. Use your own provider key, because Laravel AI’s drivers always send key as Authorization. Add the key to your application’s .env file as OPENAI_API_KEY. BaseUrl::providerUrl() builds the URL in config/ai.php:
use Meirdick\WorkersAi\Cloudflare\BaseUrl;
'openai' => [
'driver' => 'openai',
'key' => env('OPENAI_API_KEY'),
'url' => BaseUrl::providerUrl(env('CLOUDFLARE_ACCOUNT_ID'), env('CLOUDFLARE_AI_GATEWAY'), 'openai'),
// Remove this line unless the gateway is authenticated.
'headers' => ['cf-aig-authorization' => 'Bearer '.env('CLOUDFLARE_AI_GATEWAY_TOKEN')],
],Replace 'openai' with 'anthropic' or another provider slug. Do not call config() here, because config/ai.php is still loading. To use a stored key instead, use third-party models.
Use the provider path
To use <gateway>/workers-ai/v1 instead of /compat, set gateway_path:
'gateway_path' => 'workers-ai/v1',/compat stays the default. See The gateway provider path.
Use a custom URL
Set url for a full URL, such as a proxy. Add it to your application’s .env file as CLOUDFLARE_AI_URL, and read it in config/ai.php. Do not set account_id with it, because the package throws if both are set.
'workers-ai' => [
'driver' => 'workers-ai',
'key' => env('CLOUDFLARE_AI_API_TOKEN'),
'url' => env('CLOUDFLARE_AI_URL'),
],If the URL ends in /compat, the package adds the workers-ai/ model prefix, and Clef and Clairic throw. For any other URL, it sends the bare model name. See Where each call goes.
Session affinity
Set session_affinity to send the x-session-affinity header on every request:
'workers-ai' => [
// ...
'session_affinity' => 'my-app-session',
],Related requests then reach the same cache shard, which helps the model prefix cache. See the next section.
Cached tokens
Some models cache the start of a prompt, and a cache hit costs fewer neurons. Cloudflare reports the cached count in usage.prompt_tokens_details.cached_tokens.
The two laravel/ai lines count differently:
laravel/ai0.9 to 0.11.Usage::$promptTokensis the uncached count.Usage::$cacheReadInputTokensis the cached count. The two add up to theprompt_tokensthat Cloudflare reports.laravel/ai1.x. The package returnsTextUsage.inputTokensis theprompt_tokensthat Cloudflare reports, with cached tokens included.cacheReadInputTokensis the cached part of it. It isnullwhen the response has no cache field.outputTokensis the completion count.
WorkersAiUsageReported::$promptTokens is the uncached count on both lines.
// laravel/ai 0.9 to 0.11
$uncached = $response->usage->promptTokens;
$cached = $response->usage->cacheReadInputTokens;
// laravel/ai 1.x
$total = $response->usage->inputTokens;
$cached = $response->usage->cacheReadInputTokens ?? 0;
$uncached = $total - $cached;Two identical calls on @cf/zai-org/glm-5.3-flash, under one x-session-affinity value, measured on 2026-09-16:
| Call | Wire prompt_tokens |
Wire cached_tokens |
0.x promptTokens |
1.x inputTokens |
cacheReadInputTokens |
Neurons |
|---|---|---|---|---|---|---|
| 1 | 1221 | 0 | 1221 | 1221 | 0 | 17.8 |
| 2 | 1221 | 1216 | 5 | 1221 | 1216 | 8.3 |
The cache depends on the model. In tests, @cf/zai-org/glm-5.3-flash and @cf/moonshotai/kimi-k2.6 hit the cache. @cf/meta/llama-3.3-70b-instruct-fp8-fast, @cf/meta/llama-4-scout-17b-16e-instruct and @cf/openai/gpt-oss-120b did not.
#[CacheInstructions] from Laravel AI has no effect on this provider.
Custom headers
The package sends the headers array from the provider config. A configured header replaces a package header with the same name. Names match without regard to case. The package sends these headers on chat, streaming and embeddings.
'workers-ai' => [
// ...
'headers' => ['X-Trace-Id' => env('TRACE_ID')],
],On laravel/ai 1.x, Provider::withHeaders() writes to the same array, so per-call headers reach Workers AI. Before package version 1.0.0, the package dropped the headers key and per-call headers.
Logging
AI Gateway logs are in the Cloudflare dashboard. The package does not write request logs. It writes one warning to the Laravel log when it raises the token cap for a thinking request.
Gateway retries
An AI Gateway with retry_max_attempts set retries inside your single HTTP request, and the package retries too. A late error can make the request several times longer, and your own timeout can cut it. Use one retry layer. If you use the gateway retry, set retry to false in the package config. See Errors and retries.