> For the complete documentation index, see [llms.txt](https://docs.getlimy.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.getlimy.ai/limy-pixel/cdn-integration/cloudflare-worker.md).

# Cloudflare (Worker)

### Overview

This integration runs a Cloudflare Worker as middleware. On each request, the Worker extracts metadata and sends it to Agent Monitoring — without interfering with normal request flow.

{% hint style="info" %}
For additional details about Logpush, refer to Cloudflare’s official documentation.\
Cloudflare Worker is free up to 100K requests per day, for additional details, refer to [Cloudflare’s official documentation](https://developers.cloudflare.com/workers/platform/pricing/).
{% endhint %}

{% stepper %}
{% step %}
Open Terminal and install the following using npm

```
npm create cloudflare@2.37.4 -- log-shipping
```

Follow the instructions below

<figure><img src="/files/YjmlWT9eOhEyd3gRwKU6" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/aqcsZSp48Ryulc9nLktE" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/U5ydIjUGbULKneybC90M" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/w2RWDptQxIB4FIMUIY85" alt=""><figcaption></figcaption></figure>

Running the CLI creates the `log-shipping` project and handles dependency installation automatically.

**VERY IMPORTANT - DO NOT DEPLOY YET**

<figure><img src="/files/5YrGAWuHxQZ1uqFBfLF7" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

#### Configure your worker

Edit your `wrangler.jsonc` file to configure `LIMY_URL` environment variable to our endpoint and replace the `domain.com/*` placeholder should match your organization's domain. If your site is `https://www.domain.com`, use `domain.com/*` as the pattern and `domain.com` for zone\_name

```javascript
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "log-collector",
  "main": "src/index.ts",
  "compatibility_date": "2025-12-09",
  "observability": {
    "enabled": true
  },
  "route": {
    "pattern": "domain.com/*",
    "zone_name": "domain.com"
  },
  "vars": { "LIMY_URL": "https://stream.getlimy.ai" }
}
```

Then replace `src/index.ts` code within the code below :

{% hint style="danger" %}
If your unsure- contact us at <kbd><answers@limy.ai></kbd>
{% endhint %}

```tsx
/**
 * Cloudflare Worker for Log Collection
 *
 * This worker captures HTTP request/response data and forwards it to Limy's log collection API.
 * It runs as a middleware, meaning it doesn't interfere with the actual request handling.
 */

export interface Env {
     LIMY_KEY: string;
    LIMY_URL: string;
}

export default {
    async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {

    // Get the original response
    const response = await fetch(request);

    // Clone the response so we can read it multiple times
        const responseClone = response.clone();
    
        ctx.waitUntil(handleRequest(request, responseClone, env));
        return response;
    }
} satisfies ExportedHandler<Env>;

async function handleRequest(request: Request, response: Response, env: Env) {
	const requestUrl = new URL(request.url);
	const cf = request.cf;
  
	const headerSize = Array.from(response.headers.entries())
	  .reduce((total, [key, value]) => total + key.length + value.length + 4, 0);
	
	const responseBody = await response.blob();
	const bodySize = responseBody.size;

    // Total bytes sent includes headers and body
    const totalBytesSent = headerSize + bodySize;

    const logData = {
		// Timing
		timestamp: Date.now(),
		
		// Request basics
		host: requestUrl.hostname,
		method: request.method,
		pathname: requestUrl.pathname,
		query_params: Object.fromEntries(requestUrl.searchParams),
		
		// Client info
		ip: request.headers.get('cf-connecting-ip'),
		userAgent: request.headers.get('user-agent'),
		referer: request.headers.get('referer'),
		acceptLanguage: request.headers.get('accept-language'),
		
		// Response info
		bytes: headerSize + bodySize,
		status: response.status,
		contentType: response.headers.get('content-type'),
		
		// Cloudflare request ID
		rayId: request.headers.get('cf-ray'),
		
		// Geographic (from cf object)
		country: cf?.country,
		city: cf?.city,
		region: cf?.region,
		regionCode: cf?.regionCode,
		continent: cf?.continent,
		postalCode: cf?.postalCode,
		latitude: cf?.latitude,
		longitude: cf?.longitude,
		timezone: cf?.timezone,
		
		// Network
		asn: cf?.asn,
		asOrganization: cf?.asOrganization,
		colo: cf?.colo,  // Cloudflare datacenter
		
		// Connection
		httpProtocol: cf?.httpProtocol,
		tlsVersion: cf?.tlsVersion,
		tlsCipher: cf?.tlsCipher,
    }

    await fetch(env.LIMY_URL, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-API-Key': env.LIMY_KEY,
			'User-Agent': 'Cloudflare-Worker-Logs'
        },
        body: JSON.stringify([logData])
    }).catch(error => console.error('Failed to send logs:', error))
}
```

{% endstep %}

{% step %}

#### Login to Cloudflare

Deploy the worker using Wrangler CLI:

```typescript
//Login to Cloudflare
npx wrangler login
```

**Configure Limy API key**

Cloudflare Workers Secrets let you store sensitive values like API keys securely, outside of your code.

```typescript
npx wrangler secret put LIMY_KEY
```

{% endstep %}

{% step %}

#### Deploy your worker

```
npx wrangler deploy
```

<figure><img src="/files/aYpJIGcHxddezpZLL01v" alt=""><figcaption></figcaption></figure>
{% endstep %}
{% endstepper %}

### Need More Help? <a href="#additional-resources" id="additional-resources"></a>

* [Cloudflare Workers Documentation](https://developers.cloudflare.com/workers/)
* [Wrangler CLI Documentation](https://developers.cloudflare.com/workers/wrangler/)
* Contact <kbd><answers@limy.ai></kbd> for API-related questions
