.claude/skills/cloudflare/SKILL.md
Setup domains in Cloudflare with DNS for Clerk, Vercel, and email routing. Use when adding new domains, configuring DNS records, or setting up email redirects.
npx skillsauth add vitoropereira/claude-starter-kit cloudflareInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Automate Cloudflare workflows: DNS setup, Clerk integration, Vercel deployment, email routing, and R2 storage.
Option 1: API Token (Recommended)
# Add to .env.local
CLOUDFLARE_API_TOKEN="your-api-token"
CLOUDFLARE_ACCOUNT_ID="your-account-id"
Create token at: https://dash.cloudflare.com/profile/api-tokens Required permissions:
Option 2: Wrangler CLI
# Install wrangler
bun add -g wrangler
# Login (opens browser)
wrangler login
# Verify
wrangler whoami
# Vercel CLI (required)
bun add -g vercel
vercel login
When setting up a new domain, follow these steps:
Ask the user for:
example.com)my-app)contact, support)[email protected])# If using API token
curl -X GET "https://api.cloudflare.com/client/v4/zones?name=DOMAIN" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" | jq '.result[0].id'
# If using wrangler
wrangler pages project list # Shows associated zones
Clerk provides specific DNS records for each project. Common patterns:
# Example: CNAME record
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"type": "CNAME",
"name": "clerk",
"content": "frontend-api.clerk.dev",
"ttl": 1,
"proxied": false
}'
# Example: TXT record for verification
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"type": "TXT",
"name": "@",
"content": "clerk-verification=xxxxx",
"ttl": 1
}'
# Add domain to Vercel project
vercel domains add DOMAIN --scope=TEAM_SLUG
# Or link to specific project
vercel domains add DOMAIN PROJECT_NAME
Then create Vercel DNS records:
# A record for root domain
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"type": "A",
"name": "@",
"content": "76.76.21.21",
"ttl": 1,
"proxied": false
}'
# CNAME for www subdomain
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"type": "CNAME",
"name": "www",
"content": "cname.vercel-dns.com",
"ttl": 1,
"proxied": false
}'
First, enable email routing for the zone (do this in Cloudflare dashboard first time).
Then create routing rules:
# Create destination address (must be verified first)
curl -X POST "https://api.cloudflare.com/client/v4/accounts/ACCOUNT_ID/email/routing/addresses" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"email": "[email protected]"
}'
# Create routing rule for [email protected]
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/email/routing/rules" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"name": "Forward contact",
"enabled": true,
"matchers": [{"type": "literal", "field": "to", "value": "contact@DOMAIN"}],
"actions": [{"type": "forward", "value": ["[email protected]"]}]
}'
Required MX records for email routing:
# MX records for Cloudflare Email Routing
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"type": "MX",
"name": "@",
"content": "route1.mx.cloudflare.net",
"priority": 69,
"ttl": 1
}'
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"type": "MX",
"name": "@",
"content": "route2.mx.cloudflare.net",
"priority": 46,
"ttl": 1
}'
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"type": "MX",
"name": "@",
"content": "route3.mx.cloudflare.net",
"priority": 89,
"ttl": 1
}'
# TXT record for SPF
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"type": "TXT",
"name": "@",
"content": "v=spf1 include:_spf.mx.cloudflare.net ~all",
"ttl": 1
}'
After setup, verify:
# List all DNS records
curl -X GET "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" | jq '.result[] | {type, name, content}'
# Check Vercel domain status
vercel domains inspect DOMAIN
# Test email routing (send test email to contact@DOMAIN)
When running /cloudflare, ask:
What domain are you setting up?
> example.com
Paste the Clerk DNS records from your Clerk dashboard:
> [user pastes records]
What's the Vercel project name?
> my-saas-app
What email addresses should I create? (comma-separated)
> contact, support, hello
What email should these redirect to?
> [email protected]
| Type | Use Case | Proxied | |------|----------|---------| | A | Root domain to IP | No (for Vercel) | | CNAME | Subdomain to hostname | No (for Clerk/Vercel) | | TXT | Verification, SPF | N/A | | MX | Email routing | N/A |
| Issue | Solution |
|-------|----------|
| Zone not found | Domain must be added to Cloudflare first |
| DNS propagation slow | Wait 5-10 minutes, check with dig |
| Email not forwarding | Verify destination email first |
| Vercel 404 | Check DNS proxied=false for Vercel records |
| Clerk verification failed | Ensure TXT record is on root (@) |
# Check DNS propagation
dig DOMAIN +short
dig DOMAIN MX +short
dig DOMAIN TXT +short
# List zones in account
curl -X GET "https://api.cloudflare.com/client/v4/zones" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" | jq '.result[] | {name, id}'
# Delete a DNS record
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records/RECORD_ID" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
Setup R2 buckets for file storage: user uploads, static assets, backups.
Ask the user:
What do you want to do with R2?
1. Create new bucket (full setup)
2. Configure existing bucket (CORS, public access)
3. Setup custom domain for bucket
Bucket name?
> my-app-uploads
What will this bucket store?
1. User uploads (images, files) - needs CORS + presigned URLs
2. Static assets (public CDN) - needs public access
3. Backups (private) - no public access
> 1
Custom domain? (optional, press enter to skip)
> uploads.myapp.com
# Create bucket via wrangler
wrangler r2 bucket create my-app-uploads
# Or via API
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/{account_id}/r2/buckets" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{"name": "my-app-uploads", "locationHint": "wnam"}'
Create cors.json:
{
"corsRules": [
{
"allowedOrigins": ["https://myapp.com", "http://localhost:3000"],
"allowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
"allowedHeaders": ["*"],
"exposeHeaders": ["ETag", "Content-Length"],
"maxAgeSeconds": 3600
}
]
}
Apply CORS:
wrangler r2 bucket cors put my-app-uploads --file=cors.json
Option A: Enable R2.dev subdomain (via dashboard)
Option B: Custom domain:
# Add CNAME record
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"type": "CNAME",
"name": "uploads",
"content": "{account_id}.r2.cloudflarestorage.com",
"ttl": 1,
"proxied": true
}'
Then enable custom domain in R2 bucket settings.
.env.local:R2_ACCESS_KEY_ID="your-access-key"
R2_SECRET_ACCESS_KEY="your-secret-key"
R2_ENDPOINT="https://{account_id}.r2.cloudflarestorage.com"
R2_BUCKET_NAME="my-app-uploads"
# List buckets
wrangler r2 bucket list
# Create bucket
wrangler r2 bucket create BUCKET_NAME
# Delete bucket
wrangler r2 bucket delete BUCKET_NAME
# List objects
wrangler r2 object list BUCKET_NAME
# Upload file
wrangler r2 object put BUCKET_NAME/path/file.png --file=./local.png
# View CORS config
wrangler r2 bucket cors get BUCKET_NAME
| Use Case | CORS | Public | Custom Domain | |----------|------|--------|---------------| | User uploads | Yes | No | Optional | | Static assets/CDN | No | Yes | Recommended | | Backups | No | No | No | | Public downloads | No | Yes | Optional |
| Issue | Solution | |-------|----------| | CORS error in browser | Add domain to allowedOrigins | | 403 Forbidden | Check API token has R2:Edit permission | | Custom domain not working | Ensure CNAME is proxied (orange cloud) | | Upload fails | Verify Content-Type header matches file |
testing
Draft cold emails, warm intro blurbs, follow-ups, update emails, and investor communications for fundraising. Use when the user wants outreach to angels, VCs, strategic investors, or accelerators and needs concise, personalized, investor-facing messaging.
testing
Create and update pitch decks, one-pagers, investor memos, accelerator applications, financial models, and fundraising materials. Use when the user needs investor-facing documents, projections, use-of-funds tables, milestone plans, or materials that must stay internally consistent across multiple fundraising assets.
tools
iMessage/SMS CLI for listing chats, history, and sending messages via Messages.app.
development
This skill should be used when the user asks to "test for insecure direct object references," "find IDOR vulnerabilities," "exploit broken access control," "enumerate user IDs or object references," or "bypass authorization to access other users' data." Adapted for MGM-Web multi-tenant architecture.