API Requests Blocked by Cloudflare: Debugging and Solutions
The Problem
You're building a web application and suddenly encounter a confusing error: your API calls are returning strange HTML content with messages like "Why have I been blocked? The action you just performed triggered the security solution." Instead of your expected JSON response, the frontend displays raw HTML source code across the page.
This creates two immediate issues:
- Your API functionality is completely broken
- Your users see ugly HTML markup instead of proper error messages
Let's dive into what's happening and how to fix it.
Investigation
Understanding the Root Cause
This issue typically manifests as a two-part problem:
- Backend Issue: Cloudflare's security services (WAF/Bot Fight/Rate Limiting) are intercepting and blocking your API requests
- Frontend Issue: Your application isn't properly handling non-JSON error responses
When Cloudflare blocks a request, it returns an HTML error page with Content-Type: text/html, but your frontend expects JSON. Without proper error handling, the raw HTML gets rendered directly to the page.
Common Cloudflare Blocking Triggers
| Trigger | Description |
|---|---|
| Bot Fight Mode | API requests appear automated or lack proper browser headers |
| WAF Rules | Request parameters contain suspicious patterns (SQL keywords, special characters) |
| Rate Limiting | Too many requests in a short time period |
| IP Reputation | Requests from flagged IP addresses |
| Missing Headers | Lack of User-Agent, Origin, or Referer headers |
Why APIs Go Through Cloudflare
Many developers assume Cloudflare only caches static assets, but when you enable the orange cloud (proxied) setting for your domain, all HTTP(S) traffic passes through Cloudflare, including:
- API endpoints (
/api/*) - File uploads
- WebSocket connections
- GraphQL queries
This happens regardless of caching behavior - Cloudflare acts as a reverse proxy for your entire domain.
Root Cause Analysis
The problem occurs because:
- Cloudflare intercepts the API request before it reaches your server
- Security rules trigger based on request patterns, headers, or content
- Cloudflare returns an HTML error page instead of forwarding to your API
- Your frontend assumes all API responses are JSON and tries to parse HTML as text
- Error handling fails and displays raw HTML to users
Solution
Fix 1: Improve Frontend Error Handling
The immediate fix is to add proper content-type checking in your API calls:
async function makeApiCall(url, options = {}) {
try {
const response = await fetch(url, options);
const contentType = response.headers.get("content-type");
// Check if response is actually JSON
if (response.ok && contentType?.includes("application/json")) {
return await response.json();
}
// Handle non-JSON responses (like Cloudflare blocks)
const text = await response.text();
if (!response.ok) {
// Check if it's a Cloudflare block page
if (text.includes("Cloudflare") || text.includes("blocked")) {
throw new Error("Request blocked by security system. Please try again later.");
}
throw new Error(`Server error: ${response.status}`);
}
throw new Error(`Unexpected response format: ${contentType}`);
} catch (error) {
console.error("API call failed:", error);
// Show user-friendly error instead of raw HTML
throw new Error("Request failed. Please check your connection and try again.");
}
}Fix 2: Configure Cloudflare Security Rules
Navigate to your Cloudflare dashboard and adjust security settings:
Skip Security for API Routes
Security → WAF → Custom Rules:
Rule Expression: (http.request.uri.path contains "/api/")
Action: Skip - All remaining custom rules
Adjust Bot Fight Mode
Security → Bots:
- Add your frontend domain to the allowlist
- Consider switching from "Fight" to "Challenge" mode for API endpoints
Review Rate Limiting
Security → Rate Limiting:
- Increase limits for authenticated API calls
- Add bypass rules for legitimate traffic patterns
Fix 3: Use Dedicated API Subdomain
For better separation, consider using a separate subdomain for APIs:
www.example.com → Frontend (Orange Cloud ✅)
api.example.com → Backend APIs (Gray Cloud ⛅)
This completely bypasses Cloudflare for API traffic while maintaining protection for your frontend.
Fix 4: Implement Proper API Authentication
Add Cloudflare-compatible authentication:
// Add proper headers to API requests
const apiCall = fetch('/api/upload', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': 'YourApp/1.0',
'X-Requested-With': 'XMLHttpRequest',
// Add authentication token
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(data)
});Prevention and Best Practices
1. Design Cloudflare-Friendly APIs
// Good: Structured error responses
app.use((err, req, res, next) => {
res.status(err.status || 500).json({
error: true,
message: err.message,
timestamp: new Date().toISOString()
});
});
// Avoid: Plain text or HTML error responses2. Monitor Cloudflare Security Events
Regularly check Security → Events to identify:
- Which rules are triggering
- Common false positive patterns
- Traffic that should be allowlisted
3. Implement Graceful Degradation
// Fallback for blocked requests
const apiWithFallback = async (url, data) => {
try {
return await primaryApiCall(url, data);
} catch (error) {
if (error.message.includes('blocked')) {
// Try alternative endpoint or show offline message
return handleBlockedRequest();
}
throw error;
}
};4. Use Environment-Specific Configurations
const API_BASE = process.env.NODE_ENV === 'development'
? 'http://localhost:3000/api' // Direct connection
: 'https://api.example.com'; // Through CloudflareLessons Learned
- Always handle non-JSON responses in your API clients - external services can return HTML at any time
- Cloudflare proxies all traffic by default - not just static assets
- Security rules affect APIs differently than web pages - they're more likely to trigger on POST requests with large payloads
- Proper error handling improves user experience significantly during service disruptions
- Monitor security events proactively to prevent legitimate traffic from being blocked
By implementing these solutions, you'll create a more resilient application that gracefully handles Cloudflare security interventions while maintaining a good user experience.
