cloudflare
references/smart-placement/gotchas.md
.md 175 lines
Content
# Smart Placement Gotchas
## Common Errors
### "INSUFFICIENT_INVOCATIONS"
**Cause:** Not enough traffic for Smart Placement to analyze
**Solution:**
- Ensure Worker receives consistent global traffic
- Wait longer (analysis takes up to 15 minutes)
- Send test traffic from multiple global locations
- Check Worker has fetch event handler
### "UNSUPPORTED_APPLICATION"
**Cause:** Smart Placement made Worker slower rather than faster
**Reasons:**
- Worker doesn't make backend calls (runs faster at edge)
- Backend calls are cached (network latency to user more important)
- Backend service has good global distribution
- Worker serves static assets or Pages content
**Solutions:**
- Disable Smart Placement: `{ "placement": { "mode": "off" } }`
- Review whether Worker actually benefits from Smart Placement
- Consider caching strategy to reduce backend calls
- For Pages/Assets Workers, use separate backend Worker with Smart Placement
### "No request duration metrics"
**Cause:** Smart Placement not enabled, insufficient time passed, insufficient traffic, or analysis incomplete
**Solution:**
- Ensure Smart Placement enabled in config
- Wait 15+ minutes after deployment
- Verify Worker has sufficient traffic
- Check `placement_status` is `SUCCESS`
### "cf-placement header missing"
**Cause:** Smart Placement not enabled, beta feature removed, or Worker not analyzed yet
**Solution:** Verify Smart Placement enabled, wait for analysis (15min), check if beta feature still available
## Pages/Assets + Smart Placement Performance Degradation
**Problem:** Static assets load 2-5x slower when Smart Placement enabled with `run_worker_first = true`.
**Cause:** Smart Placement routes ALL requests (including static assets like HTML, CSS, JS, images) to remote locations. Static content should ALWAYS be served from edge closest to user.
**Solution:** Split into separate Workers OR disable Smart Placement:
```jsonc
// ❌ BAD - Assets routed away from user
{
"name": "pages-app",
"placement": { "mode": "smart" },
"assets": { "run_worker_first": true }
}
// ✅ GOOD - Assets at edge, API optimized
// frontend/wrangler.jsonc
{
"name": "frontend",
"assets": { "run_worker_first": true }
// No placement field - stays at edge
}
// backend/wrangler.jsonc
{
"name": "backend-api",
"placement": { "mode": "smart" }
}
```
This is one of the most common and impactful Smart Placement misconfigurations.
## Monolithic Full-Stack Worker
**Problem:** Frontend and backend logic in single Worker with Smart Placement enabled.
**Cause:** Smart Placement optimizes for backend latency but increases user-facing response time.
**Solution:** Split into two Workers:
```jsonc
// frontend/wrangler.jsonc
{
"name": "frontend",
"placement": { "mode": "off" }, // Explicit: stay at edge
"services": [{ "binding": "BACKEND", "service": "backend-api" }]
}
// backend/wrangler.jsonc
{
"name": "backend-api",
"placement": { "mode": "smart" },
"d1_databases": [{ "binding": "DB", "database_id": "xxx" }]
}
```
## Local Development Confusion
**Issue:** Smart Placement doesn't work in `wrangler dev`.
**Explanation:** Smart Placement only activates in production deployments, not local development.
**Solution:** Test Smart Placement in staging environment: `wrangler deploy --env staging`
## Baseline Traffic & Analysis Time
**Note:** Smart Placement routes 1% of requests WITHOUT optimization for comparison (expected).
**Analysis time:** Up to 15 minutes. During analysis, Worker runs at edge. Monitor `placement_status`.
## RPC Methods Not Affected (Critical Limitation)
**Problem:** Enabled Smart Placement on backend but RPC calls still slow.
**Cause:** Smart Placement ONLY affects `fetch` handlers. RPC methods (Service Bindings with `WorkerEntrypoint`) are NEVER affected.
**Why:** RPC bypasses `fetch` handler - Smart Placement can only route `fetch` requests.
**Solution:** Convert to fetch-based Service Bindings:
```typescript
// ❌ RPC - Smart Placement has NO EFFECT
export class BackendRPC extends WorkerEntrypoint {
async getData() {
// ALWAYS runs at edge
return await this.env.DATABASE.prepare('SELECT * FROM table').all();
}
}
// ✅ Fetch - Smart Placement WORKS
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Runs close to DATABASE when Smart Placement enabled
const data = await env.DATABASE.prepare('SELECT * FROM table').all();
return Response.json(data);
}
}
```
## Requirements
- **Wrangler 2.20.0+** required
- **Consistent multi-region traffic** needed for analysis
- **Only affects fetch handlers** - RPC methods and named entrypoints not affected
## Limits
| Resource/Limit | Value | Notes |
|----------------|-------|-------|
| Analysis time | Up to 15 minutes | After enabling |
| Baseline traffic | 1% | Routed without optimization |
| Min Wrangler version | 2.20.0+ | Required |
| Traffic requirement | Multi-region | Consistent needed |
## Disabling Smart Placement
```jsonc
{ "placement": { "mode": "off" } } // Explicit disable
// OR remove "placement" field entirely (same effect)
```
Both behaviors identical - Worker runs at edge closest to user.
## When NOT to Use Smart Placement
- Workers serving only static content or cached responses
- Workers without significant backend communication
- Pure edge logic (auth checks, redirects, simple transformations)
- Workers without fetch event handlers
- Pages/Assets Workers with `run_worker_first = true`
- Workers using RPC methods instead of fetch handlers
These scenarios won't benefit and may perform worse with Smart Placement.