update skills

This commit is contained in:
2026-03-17 16:53:22 -07:00
parent 0b0783ef8e
commit f9a530667e
389 changed files with 54512 additions and 1 deletions

View File

@@ -0,0 +1,68 @@
# Cloudflare Snippets Skill Reference
## Description
Expert guidance for **Cloudflare Snippets ONLY** - a lightweight JavaScript-based edge logic platform for modifying HTTP requests and responses. Snippets run as part of the Ruleset Engine and are included at no additional cost on paid plans (Pro, Business, Enterprise).
## What Are Snippets?
Snippets are JavaScript functions executed at the edge as part of Cloudflare's Ruleset Engine. Key characteristics:
- **Execution time**: 5ms CPU limit per request
- **Size limit**: 32KB per snippet
- **Runtime**: V8 isolate (subset of Workers APIs)
- **Subrequests**: 2-5 fetch calls depending on plan
- **Cost**: Included with Pro/Business/Enterprise plans
## Snippets vs Workers Decision Matrix
| Factor | Choose Snippets If... | Choose Workers If... |
|--------|----------------------|---------------------|
| **Complexity** | Simple request/response modifications | Complex business logic, routing, middleware |
| **Execution time** | <5ms sufficient | Need >5ms or variable time |
| **Subrequests** | 2-5 fetch calls sufficient | Need >5 subrequests or complex orchestration |
| **Code size** | <32KB sufficient | Need >32KB or npm dependencies |
| **Cost** | Want zero additional cost | Can afford $5/mo + usage |
| **APIs** | Need basic fetch, headers, URL | Need KV, D1, R2, Durable Objects, cron triggers |
| **Deployment** | Need rule-based triggers | Want custom routing logic |
**Rule of thumb**: Use Snippets for modifications, Workers for applications.
## Execution Model
1. Request arrives at Cloudflare edge
2. Ruleset Engine evaluates snippet rules (filter expressions)
3. If rule matches, snippet executes within 5ms limit
4. Modified request/response continues through pipeline
5. Response returned to client
Snippets execute synchronously in the request path - performance is critical.
## Reading Order
1. **[configuration.md](configuration.md)** - Start here: setup, deployment methods (Dashboard/API/Terraform)
2. **[api.md](api.md)** - Core APIs: Request, Response, headers, `request.cf` properties
3. **[patterns.md](patterns.md)** - Real-world examples: geo-routing, A/B tests, security headers
4. **[gotchas.md](gotchas.md)** - Troubleshooting: common errors, performance tips, API limitations
## In This Reference
- **[configuration.md](configuration.md)** - Setup, deployment, configuration
- **[api.md](api.md)** - API endpoints, methods, interfaces
- **[patterns.md](patterns.md)** - Common patterns, use cases, examples
- **[gotchas.md](gotchas.md)** - Troubleshooting, best practices, limitations
## Quick Start
```javascript
// Snippet: Add security headers
export default {
async fetch(request) {
const response = await fetch(request);
const newResponse = new Response(response.body, response);
newResponse.headers.set("X-Frame-Options", "DENY");
newResponse.headers.set("X-Content-Type-Options", "nosniff");
return newResponse;
}
}
```
Deploy via Dashboard (Rules → Snippets) or API/Terraform. See configuration.md for details.
## See Also
- [Cloudflare Docs](https://developers.cloudflare.com/rules/snippets/)

View File

@@ -0,0 +1,198 @@
# Snippets API Reference
## Request Object
### HTTP Properties
```javascript
request.method // GET, POST, PUT, DELETE, etc.
request.url // Full URL string
request.headers // Headers object
request.body // ReadableStream (for POST/PUT)
request.cf // Cloudflare properties (see below)
```
### URL Operations
```javascript
const url = new URL(request.url);
url.hostname // "example.com"
url.pathname // "/path/to/page"
url.search // "?query=value"
url.searchParams.get("q") // "value"
url.searchParams.set("q", "new")
url.searchParams.delete("q")
```
### Header Operations
```javascript
// Read headers
request.headers.get("User-Agent")
request.headers.has("Authorization")
request.headers.getSetCookie() // Get all Set-Cookie headers
// Modify headers (create new request)
const modifiedRequest = new Request(request);
modifiedRequest.headers.set("X-Custom", "value")
modifiedRequest.headers.delete("X-Remove")
```
### Cloudflare Properties (`request.cf`)
Access Cloudflare-specific metadata about the request:
```javascript
// Geolocation
request.cf.city // "San Francisco"
request.cf.continent // "NA"
request.cf.country // "US"
request.cf.region // "California" or "CA"
request.cf.regionCode // "CA"
request.cf.postalCode // "94102"
request.cf.latitude // "37.7749"
request.cf.longitude // "-122.4194"
request.cf.timezone // "America/Los_Angeles"
request.cf.metroCode // "807" (DMA code)
// Network
request.cf.colo // "SFO" (airport code of datacenter)
request.cf.asn // 13335 (ASN number)
request.cf.asOrganization // "Cloudflare, Inc."
// Bot Management (if enabled)
request.cf.botManagement.score // 1-99 (1=bot, 99=human)
request.cf.botManagement.verified_bot // true/false
request.cf.botManagement.static_resource // true/false
// TLS/HTTP version
request.cf.tlsVersion // "TLSv1.3"
request.cf.tlsCipher // "AEAD-AES128-GCM-SHA256"
request.cf.httpProtocol // "HTTP/2"
// Request metadata
request.cf.requestPriority // "weight=192;exclusive=0"
```
**Use cases**: Geo-routing, bot detection, security decisions, analytics.
## Response Object
### Response Constructors
```javascript
// Plain text
new Response("Hello", { status: 200 })
// JSON
Response.json({ key: "value" }, { status: 200 })
// HTML
new Response("<h1>Hi</h1>", {
status: 200,
headers: { "Content-Type": "text/html" }
})
// Redirect
Response.redirect("https://example.com", 301) // or 302
// Stream (pass through)
new Response(response.body, response)
```
### Response Headers
```javascript
// Create modified response
const newResponse = new Response(response.body, response);
// Set/modify headers
newResponse.headers.set("X-Custom", "value")
newResponse.headers.append("Set-Cookie", "session=abc; Path=/")
newResponse.headers.delete("Server")
// Common headers
newResponse.headers.set("Cache-Control", "public, max-age=3600")
newResponse.headers.set("Content-Type", "application/json")
```
### Response Properties
```javascript
response.status // 200, 404, 500, etc.
response.statusText // "OK", "Not Found", etc.
response.headers // Headers object
response.body // ReadableStream
response.ok // true if status 200-299
response.redirected // true if redirected
```
## REST API Operations
### List Snippets
```bash
GET /zones/{zone_id}/snippets
```
### Get Snippet
```bash
GET /zones/{zone_id}/snippets/{snippet_name}
```
### Create/Update Snippet
```bash
PUT /zones/{zone_id}/snippets/{snippet_name}
Content-Type: multipart/form-data
files=@snippet.js
metadata={"main_module":"snippet.js"}
```
### Delete Snippet
```bash
DELETE /zones/{zone_id}/snippets/{snippet_name}
```
### List Snippet Rules
```bash
GET /zones/{zone_id}/rulesets/phases/http_request_snippets/entrypoint
```
### Update Snippet Rules
```bash
PUT /zones/{zone_id}/snippets/snippet_rules
Content-Type: application/json
{
"rules": [{
"description": "Apply snippet",
"enabled": true,
"expression": "http.host eq \"example.com\"",
"snippet_name": "my_snippet"
}]
}
```
## Available APIs in Snippets
### ✅ Supported
- `fetch()` - HTTP requests (2-5 subrequests per plan)
- `Request` / `Response` - Standard Web APIs
- `URL` / `URLSearchParams` - URL manipulation
- `Headers` - Header manipulation
- `TextEncoder` / `TextDecoder` - Text encoding
- `crypto.subtle` - Web Crypto API (hashing, signing)
- `crypto.randomUUID()` - UUID generation
### ❌ Not Supported in Snippets
- `caches` API - Not available (use Workers)
- `KV`, `D1`, `R2` - Storage APIs (use Workers)
- `Durable Objects` - Stateful objects (use Workers)
- `WebSocket` - WebSocket upgrades (use Workers)
- `HTMLRewriter` - HTML parsing (use Workers)
- `import` statements - No module imports
- `addEventListener` - Use `export default { async fetch() {}` pattern
## Snippet Structure
```javascript
export default {
async fetch(request) {
// Your logic here
const response = await fetch(request);
return response; // or modified response
}
}
```

View File

@@ -0,0 +1,227 @@
# Snippets Configuration Guide
## Configuration Methods
### 1. Dashboard (GUI)
**Best for**: Quick tests, single snippets, visual rule building
```
1. Go to zone → Rules → Snippets
2. Click "Create Snippet" or select template
3. Enter snippet name (a-z, 0-9, _ only, cannot change later)
4. Write JavaScript code (32KB max)
5. Configure snippet rule:
- Expression Builder (visual) or Expression Editor (text)
- Use Ruleset Engine filter expressions
6. Test with Preview/HTTP tabs
7. Deploy or Save as Draft
```
### 2. REST API
**Best for**: CI/CD, automation, programmatic management
```bash
# Create/update snippet
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/snippets/$SNIPPET_NAME" \
--request PUT \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
--form "files=@example.js" \
--form "metadata={\"main_module\": \"example.js\"}"
# Create snippet rule
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/snippets/snippet_rules" \
--request PUT \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"rules": [
{
"description": "Trigger snippet on /api paths",
"enabled": true,
"expression": "starts_with(http.request.uri.path, \"/api/\")",
"snippet_name": "api_snippet"
}
]
}'
# List snippets
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/snippets" \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
# Delete snippet
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/snippets/$SNIPPET_NAME" \
--request DELETE \
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```
### 3. Terraform
**Best for**: Infrastructure-as-code, multi-zone deployments
```hcl
# Configure Terraform provider
terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
}
provider "cloudflare" {
api_token = var.cloudflare_api_token
}
# Create snippet
resource "cloudflare_snippet" "security_headers" {
zone_id = var.zone_id
name = "security_headers"
main_module = "security_headers.js"
files {
name = "security_headers.js"
content = file("${path.module}/snippets/security_headers.js")
}
}
# Create snippet rule
resource "cloudflare_snippet_rules" "security_rules" {
zone_id = var.zone_id
rules {
description = "Apply security headers to all requests"
enabled = true
expression = "true"
snippet_name = cloudflare_snippet.security_headers.name
}
}
```
### 4. Pulumi
**Best for**: Multi-cloud IaC, TypeScript/Python/Go workflows
```typescript
import * as cloudflare from "@pulumi/cloudflare";
import * as fs from "fs";
// Create snippet
const securitySnippet = new cloudflare.Snippet("security-headers", {
zoneId: zoneId,
name: "security_headers",
mainModule: "security_headers.js",
files: [{
name: "security_headers.js",
content: fs.readFileSync("./snippets/security_headers.js", "utf8"),
}],
});
// Create snippet rule
const snippetRule = new cloudflare.SnippetRules("security-rules", {
zoneId: zoneId,
rules: [{
description: "Apply security headers",
enabled: true,
expression: "true",
snippetName: securitySnippet.name,
}],
});
```
## Filter Expressions
Snippets use Cloudflare's Ruleset Engine expression language to determine when to execute.
### Common Expression Patterns
```javascript
// Host matching
http.host eq "example.com"
http.host in {"example.com" "www.example.com"}
http.host contains "example"
// Path matching
http.request.uri.path eq "/api/users"
starts_with(http.request.uri.path, "/api/")
ends_with(http.request.uri.path, ".json")
matches(http.request.uri.path, "^/api/v[0-9]+/")
// Query parameters
http.request.uri.query contains "debug=true"
// Headers
http.headers["user-agent"] contains "Mobile"
http.headers["accept-language"] eq "en-US"
// Cookies
http.cookie contains "session="
// Geolocation
ip.geoip.country eq "US"
ip.geoip.continent eq "EU"
// Bot detection (requires Bot Management)
cf.bot_management.score lt 30
// Method
http.request.method eq "POST"
http.request.method in {"POST" "PUT" "PATCH"}
// Combine with logical operators
http.host eq "example.com" and starts_with(http.request.uri.path, "/api/")
ip.geoip.country eq "US" or ip.geoip.country eq "CA"
not http.headers["user-agent"] contains "bot"
```
### Expression Functions
| Function | Example | Description |
|----------|---------|-------------|
| `starts_with()` | `starts_with(http.request.uri.path, "/api/")` | Check prefix |
| `ends_with()` | `ends_with(http.request.uri.path, ".json")` | Check suffix |
| `contains()` | `contains(http.headers["user-agent"], "Mobile")` | Check substring |
| `matches()` | `matches(http.request.uri.path, "^/api/")` | Regex match |
| `lower()` | `lower(http.host) eq "example.com"` | Convert to lowercase |
| `upper()` | `upper(http.headers["x-api-key"])` | Convert to uppercase |
| `len()` | `len(http.request.uri.path) gt 100` | String length |
## Deployment Workflow
### Development
1. Write snippet code locally
2. Test syntax with `node snippet.js` or TypeScript compiler
3. Deploy to Dashboard or use API with `Save as Draft`
4. Test with Preview/HTTP tabs in Dashboard
5. Enable rule when ready
### Production
1. Store snippet code in version control
2. Use Terraform/Pulumi for reproducible deployments
3. Deploy to staging zone first
4. Test with real traffic (use low-traffic subdomain)
5. Apply to production zone
6. Monitor with Analytics/Logpush
## Limits & Requirements
| Resource | Limit | Notes |
|----------|-------|-------|
| Snippet size | 32 KB | Per snippet, compressed |
| Snippet name | 64 chars | `a-z`, `0-9`, `_` only, immutable |
| Snippets per zone | 20 | Soft limit, contact support for more |
| Rules per zone | 20 | One rule per snippet typical |
| Expression length | 4096 chars | Per rule expression |
## Authentication
### API Token (Recommended)
```bash
# Create token at: https://dash.cloudflare.com/profile/api-tokens
# Required permissions: Zone.Snippets:Edit, Zone.Rules:Edit
export CLOUDFLARE_API_TOKEN="your_token_here"
```
### API Key (Legacy)
```bash
export CLOUDFLARE_EMAIL="your@email.com"
export CLOUDFLARE_API_KEY="your_global_api_key"
```

View File

@@ -0,0 +1,86 @@
# Gotchas & Best Practices
## Common Errors
### 1000: "Snippet execution failed"
Runtime error or syntax error. Wrap code in try/catch:
```javascript
try { return await fetch(request); }
catch (error) { return new Response(`Error: ${error.message}`, { status: 500 }); }
```
### 1100: "Exceeded execution limit"
Code takes >5ms CPU. Simplify logic or move to Workers.
### 1201: "Multiple origin fetches"
Call `fetch(request)` exactly once:
```javascript
// ❌ Multiple origin fetches
const r1 = await fetch(request); const r2 = await fetch(request);
// ✅ Single fetch, reuse response
const response = await fetch(request);
```
### 1202: "Subrequest limit exceeded"
Pro: 2 subrequests, Business/Enterprise: 5. Reduce fetch calls.
### "Cannot set property on immutable object"
Clone before modifying:
```javascript
const modifiedRequest = new Request(request);
modifiedRequest.headers.set("X-Custom", "value");
```
### "caches is not defined"
Cache API NOT available in Snippets. Use Workers.
### "Module not found"
Snippets don't support `import`. Use inline code or Workers.
## Best Practices
### Performance
- Keep code <10KB (32KB limit)
- Optimize for 5ms CPU
- Clone only when modifying
- Minimize subrequests
### Security
- Validate all inputs
- Use Web Crypto API for hashing
- Sanitize headers before origin
- Don't log secrets
### Debugging
```javascript
newResponse.headers.set("X-Debug-Country", request.cf.country);
```
```bash
curl -H "X-Test: true" https://example.com -v
```
## Available APIs
**✅ Available:** `fetch()`, `Request`, `Response`, `Headers`, `URL`, `crypto.subtle`, `crypto.randomUUID()`, `atob()`/`btoa()`, `JSON`
**❌ NOT Available:** `caches`, `KV`, `D1`, `R2`, `Durable Objects`, `WebSocket`, `HTMLRewriter`, `import`, Node.js APIs
## Limits
| Resource | Limit |
|----------|-------|
| Snippet size | 32KB |
| Execution time | 5ms CPU |
| Subrequests (Pro/Biz) | 2/5 |
| Snippets/zone | 20 |
## Performance Benchmarks
| Operation | Time |
|-----------|------|
| Header set | <0.1ms |
| URL parsing | <0.2ms |
| fetch() | 1-3ms |
| SHA-256 | 0.5-1ms |
**Migrate to Workers when:** >5ms needed, >5 subrequests, need storage (KV/D1/R2), need npm packages, >32KB code

View File

@@ -0,0 +1,135 @@
# Snippets Patterns
## Security Headers
```javascript
export default {
async fetch(request) {
const response = await fetch(request);
const newResponse = new Response(response.body, response);
newResponse.headers.set("X-Frame-Options", "DENY");
newResponse.headers.set("X-Content-Type-Options", "nosniff");
newResponse.headers.delete("X-Powered-By");
return newResponse;
}
}
```
**Rule:** `true` (all requests)
## Geo-Based Routing
```javascript
export default {
async fetch(request) {
const country = request.cf.country;
if (["GB", "DE", "FR"].includes(country)) {
const url = new URL(request.url);
url.hostname = url.hostname.replace(".com", ".eu");
return Response.redirect(url.toString(), 302);
}
return fetch(request);
}
}
```
## A/B Testing
```javascript
export default {
async fetch(request) {
const cookies = request.headers.get("Cookie") || "";
let variant = cookies.match(/ab_test=([AB])/)?.[1] || (Math.random() < 0.5 ? "A" : "B");
const req = new Request(request);
req.headers.set("X-Variant", variant);
const response = await fetch(req);
if (!cookies.includes("ab_test=")) {
const newResponse = new Response(response.body, response);
newResponse.headers.append("Set-Cookie", `ab_test=${variant}; Path=/; Secure`);
return newResponse;
}
return response;
}
}
```
## Bot Detection
```javascript
export default {
async fetch(request) {
const botScore = request.cf.botManagement?.score;
if (botScore && botScore < 30) return new Response("Denied", { status: 403 });
return fetch(request);
}
}
```
**Requires:** Bot Management plan
## API Auth Header Injection
```javascript
export default {
async fetch(request) {
if (new URL(request.url).pathname.startsWith("/api/")) {
const req = new Request(request);
req.headers.set("X-Internal-Auth", "secret_token");
req.headers.delete("Authorization");
return fetch(req);
}
return fetch(request);
}
}
```
## CORS Headers
```javascript
export default {
async fetch(request) {
if (request.method === "OPTIONS") {
return new Response(null, {
status: 204,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE",
"Access-Control-Allow-Headers": "Content-Type, Authorization"
}
});
}
const response = await fetch(request);
const newResponse = new Response(response.body, response);
newResponse.headers.set("Access-Control-Allow-Origin", "*");
return newResponse;
}
}
```
## Maintenance Mode
```javascript
export default {
async fetch(request) {
if (request.headers.get("X-Bypass-Token") === "admin") return fetch(request);
return new Response("<h1>Maintenance</h1>", {
status: 503,
headers: { "Content-Type": "text/html", "Retry-After": "3600" }
});
}
}
```
## Pattern Selection
| Pattern | Complexity | Use Case |
|---------|-----------|----------|
| Security Headers | Low | All sites |
| Geo-Routing | Low | Regional content |
| A/B Testing | Medium | Experiments |
| Bot Detection | Medium | Requires Bot Management |
| API Auth | Low | Backend protection |
| CORS | Low | API endpoints |
| Maintenance | Low | Deployments |