cloudflare-deploy — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited cloudflare-deploy (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Guide for setting up and deploying Astro sites to Cloudflare Workers with custom domains. This encodes tested patterns that avoid common pitfalls.
Never run `wrangler deploy` or `wrangler pages deploy` locally. Deployments happen by pushing to git. The CI/CD pipeline (Cloudflare dashboard connected to GitHub) handles the rest. Local deploys create version drift and bypass any CI checks.
The only local wrangler commands you should run are diagnostic/read-only ones like wrangler whoami, wrangler deployments list, and wrangler dev (local preview).
Work through these steps in order. Each step has a verification check.
npm install @astrojs/cloudflareAstro 6 also requires an explicit Vite 7 dependency to avoid build errors (require_dist is not a function):
npm install vite@^7Verify: package.json has both @astrojs/cloudflare and vite: "^7" in dependencies.
The adapter should only activate for builds, not during local dev. Use this conditional pattern:
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({
site: 'https://your-domain.example.com',
adapter: process.argv.includes('dev') ? undefined : cloudflare(),
vite: {
plugins: [tailwindcss()],
},
});The process.argv.includes('dev') check means astro dev runs without the adapter (faster, no worker emulation), while astro build uses it.
Verify: Run npm run dev and confirm no Cloudflare-related warnings in the console.
name = "your-project-name"
compatibility_date = "2025-03-28"
compatibility_flags = ["nodejs_compat"]
[assets]
directory = "./dist"Pick a name that matches your Cloudflare Workers project name. This is the identifier Cloudflare uses, not the display name.
Verify: File exists at project root alongside package.json.
Ensure these entries are present:
node_modules/
dist/
.astro/
.wrangler/
.gstack/
.env
.env.*
.dev.vars*
!.dev.vars.example
!.env.example
.DS_StoreThe .wrangler/ directory contains local build artifacts and deployment state that should never be committed.
Ensure package.json has these scripts:
{
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "npm run build && wrangler dev"
}
}The preview script builds and then runs the Cloudflare worker locally so you can test the production behavior before pushing.
npm run buildA successful build will show:
[build] adapter: @astrojs/cloudflare[build] Complete!Common build failures:
vite: "^7" in dependencies. Install it.npm install @astrojs/cloudflare.client:only directives.git add -A
git commit -m "chore: configure Cloudflare Workers deployment"
git pushThe Cloudflare dashboard should be connected to the GitHub repo with:
npm run builddistmainAdd a routes entry to wrangler.toml:
routes = [
{ pattern = "subdomain.yourdomain.com", custom_domain = true }
]Then commit and push. Cloudflare will automatically create the DNS record if the domain's zone is managed by Cloudflare.
routes = [
{ pattern = "www.yourdomain.com", custom_domain = true },
{ pattern = "yourdomain.com", custom_domain = true }
]After pushing a deployment, verify everything is working. Use the wrangler CLI and standard tools to diagnose issues without deploying locally.
Check that the latest deployment went through:
npx wrangler deployments list --name your-project-nameThis shows all deployments with timestamps, authors, and version IDs. The most recent entry should match your latest push.
Every worker gets a *.workers.dev URL automatically. Test it:
curl -sI https://your-project-name.<account>.workers.devYou should get a 200 response. If this works but the custom domain doesn't, the issue is DNS, not the deployment.
If the custom domain doesn't resolve:
Step 1: Check DNS resolution
dig subdomain.yourdomain.com +shortIf empty, there's no DNS record yet.
Step 2: Check if Cloudflare created the record
When using custom_domain = true in routes, Cloudflare should auto-create a DNS record in the zone. Check via the API:
# List DNS records for the zone
npx wrangler dns list yourdomain.comOr check the Cloudflare dashboard: DNS > Records for the domain zone. Look for a CNAME or A record for the subdomain.
Step 3: Create the record manually if needed
If no record was auto-created, add a CNAME manually in the Cloudflare DNS dashboard:
subdomain (just the subdomain part, not the full domain)your-project-name.<account>.workers.devStep 4: Wait and re-check
DNS propagation can take a few minutes even within Cloudflare. Re-check:
dig subdomain.yourdomain.com +short
curl -sI https://subdomain.yourdomain.comStep 5: SSL issues
Cloudflare handles SSL automatically for proxied records. If you see certificate errors:
Once DNS resolves, do a full check:
# Check HTTP response and headers
curl -sI https://subdomain.yourdomain.com
# Verify correct content is served
curl -s https://subdomain.yourdomain.com | head -20
# Check a subpage works
curl -sI https://subdomain.yourdomain.com/some-pageLook for:
HTTP/2 200 statuscf-ray header (confirms Cloudflare is serving)server: cloudflare header| Symptom | Cause | Fix |
|---|---|---|
Could not resolve host | No DNS record | Add CNAME in Cloudflare DNS |
| Workers.dev works, custom domain 404s | Route not configured | Add routes to wrangler.toml, push |
| 522 Connection timed out | Worker crash or timeout | Check wrangler tail for errors |
| 1101 Worker threw exception | Runtime error in worker | Check wrangler tail --name your-project-name |
| ERR_SSL_VERSION_OR_CIPHER_MISMATCH | DNS record not proxied | Enable proxy (orange cloud) in DNS |
| Old content after push | Cache or deploy not triggered | Check wrangler deployments list, purge cache in dashboard |
To see real-time errors from the deployed worker:
npx wrangler tail --name your-project-nameThis streams logs from the production worker. Useful for debugging 500 errors, missing routes, or runtime exceptions.
For secrets (API keys, tokens), use the Cloudflare dashboard or:
npx wrangler secret put MY_SECRET --name your-project-nameFor non-secret config, add to wrangler.toml:
[vars]
PUBLIC_SITE_URL = "https://yourdomain.com"After confirming the custom domain works, you can disable the .workers.dev URL:
workers_dev = falseCommit and push.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.