Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/workers-vpc/gotchas.md
1# Gotchas and Troubleshooting23Common pitfalls, limitations, and solutions for TCP Sockets in Cloudflare Workers.45## Platform Limits67### Connection Limits89| Limit | Value |10|-------|-------|11| Max concurrent sockets per request | 6 (hard limit) |12| Socket lifetime | Request duration |13| Connection timeout | Platform-dependent, no setting |1415**Problem:** Exceeding 6 connections throws error1617**Solution:** Process in batches of 61819```typescript20for (let i = 0; i < hosts.length; i += 6) {21const batch = hosts.slice(i, i + 6).map(h => connect({ hostname: h, port: 443 }));22await Promise.all(batch.map(async s => { /* use */ await s.close(); }));23}24```2526### Blocked Destinations2728Cloudflare IPs (1.1.1.1), localhost (127.0.0.1), port 25 (SMTP), Worker's own URL blocked for security.2930**Solution:** Use public IPs or Tunnel hostnames: `connect({ hostname: "db.internal.company.net", port: 5432 })`3132### Scope Requirements3334**Problem:** Sockets created in global scope fail3536**Cause:** Sockets tied to request lifecycle3738**Solution:** Create inside handler: `export default { async fetch() { const socket = connect(...); } }`3940## Common Errors4142### Error: "proxy request failed"4344**Causes:** Blocked destination (Cloudflare IP, localhost, port 25), DNS failure, network unreachable4546**Solution:** Validate destinations, use Tunnel hostnames, catch errors with try/catch4748### Error: "TCP Loop detected"4950**Cause:** Worker connecting to itself5152**Solution:** Connect to external service, not Worker's own hostname5354### Error: "Port 25 prohibited"5556**Cause:** SMTP port blocked5758**Solution:** Use Email Workers API for email5960### Error: "socket is not open"6162**Cause:** Read/write after close6364**Solution:** Always use try/finally to ensure proper closure order6566### Error: Connection timeout6768**Cause:** No built-in timeout6970**Solution:** Use `Promise.race()`:7172```typescript73const socket = connect(addr, opts);74const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 5000));75await Promise.race([socket.opened, timeout]);76```7778## TLS/SSL Issues7980### StartTLS Timing8182**Problem:** Calling `startTls()` too early8384**Solution:** Send protocol-specific STARTTLS command, wait for server OK, then call `socket.startTls()`8586### Certificate Validation8788**Problem:** Self-signed certs fail8990**Solution:** Use proper certs or Tunnel (handles TLS termination)9192## Performance Issues9394### Not Using Connection Pooling9596**Problem:** New connection overhead per request9798**Solution:** Use [Hyperdrive](../hyperdrive/) for databases (built-in pooling)99100### Not Using Smart Placement101102**Problem:** High latency to backend103104**Solution:** Enable: `{ "placement": { "mode": "smart" } }` in wrangler.jsonc105106### Forgetting to Close Sockets107108**Problem:** Resource leaks109110**Solution:** Always use try/finally:111112```typescript113const socket = connect({ hostname: "api.internal", port: 443 });114try {115// Use socket116} finally {117await socket.close();118}119```120121## Data Handling Issues122123### Assuming Single Read Gets All Data124125**Problem:** Only reading once may miss chunked data126127**Solution:** Loop `reader.read()` until `done === true` (see patterns.md)128129### Text Encoding Issues130131**Problem:** Using wrong encoding132133**Solution:** Specify encoding: `new TextDecoder('iso-8859-1').decode(data)`134135## Security Issues136137### SSRF Vulnerability138139**Problem:** User-controlled destinations allow access to internal services140141**Solution:** Validate against strict allowlist:142143```typescript144const ALLOWED = ['api1.internal.net', 'api2.internal.net'];145const host = new URL(req.url).searchParams.get('host');146if (!host || !ALLOWED.includes(host)) return new Response('Forbidden', { status: 403 });147```148149## When to Use Alternatives150151| Use Case | Alternative | Reason |152|----------|-------------|--------|153| PostgreSQL/MySQL | [Hyperdrive](../hyperdrive/) | Connection pooling, caching |154| HTTP/HTTPS | `fetch()` | Simpler, built-in |155| HTTP with SSRF protection | VPC Services (beta 2025+) | Declarative bindings |156157## Debugging Tips1581591. **Log connection details:** `const info = await socket.opened; console.log(info.remoteAddress);`1602. **Test with public services first:** Use tcpbin.com:4242 echo server1613. **Verify Tunnel:** `cloudflared tunnel info <name>` and `cloudflared tunnel route ip list`162163## Related164165- [Hyperdrive](../hyperdrive/) - Database connections166- [Smart Placement](../smart-placement/) - Latency optimization167- [Tunnel Troubleshooting](../tunnel/gotchas.md)168