Fix "Failed to Fetch" Error in JavaScript — CORS, HTTPS, and Network Issues

TypeError: Failed to fetch is a generic browser error that masks the real problem. It can mean CORS block, network offline, HTTPS mixed content, DNS failure, SSL certificate error, or server down. This guide covers every cause with a diagnostic checklist and specific fixes for each scenario.

6+

possible root causes covered

CORS

most common cause in development

Network tab

diagnose in 30 seconds

100%

fixable once the cause is identified

The error message

TypeError: Failed to fetch — or in Firefox — TypeError: NetworkError when attempting to fetch resource. — or in Safari — TypeError: Load failed
1

Diagnose in 30 Seconds

1

Open DevTools → Network tab

Reproduce the error with DevTools open. Find the failed request. It will show in red or as "(failed)" in the Status column.

2

Read the Status column

Red "(failed)" with no status code = browser blocked it before the server. 4xx/5xx status code = server received it and returned an error. No entry at all = request was never sent (code error or extension blocking).

3

Read the Console message

The console message immediately after "Failed to fetch" usually specifies the cause: "blocked by CORS policy", "Mixed Content", "ERR_NAME_NOT_RESOLVED", "ERR_CONNECTION_REFUSED", or "SSL_ERROR".

4

Check the request URL

Is it http:// on an https:// page? Is the hostname correct? Is it localhost when you're on a deployed domain? URL misconfiguration causes 40%+ of these errors.

5

Try the same URL in curl

Run: curl -v https://your-api/endpoint. If curl succeeds but browser fails, it's a browser security restriction (CORS, mixed content, or extension) rather than a server issue.

6

Test in incognito mode

Open an incognito/private window which disables browser extensions. If the request succeeds in incognito, an extension (ad blocker, VPN, privacy tool) is the cause.

The key insight

"Failed to fetch" is a browser-level error thrown when the fetch() call itself cannot complete. The browser logs the real cause in the console — always read the full console error, not just the first line. The Network tab shows you what actually happened to the request.

2

Cause 1 — CORS Policy Block

The most common cause in development. The browser blocks the response because the server did not include the required CORS headers. Note: the request often does reach the server — the browser blocks the response.

No CORS headers → browser blocks response

❌ Bad
// Console shows: "blocked by CORS policy: No 'Access-Control-Allow-Origin' header"
fetch('https://api.example.com/data')
  .then(r => r.json())
  .catch(e => console.error(e)); // "Failed to fetch"

// IMPORTANT: The request DID reach the server.
// The browser blocked reading the response.

Add CORS headers on server or use a proxy

✅ Good
// Fix 1: Add CORS headers on the server (Express):
const cors = require('cors');
app.use(cors({
  origin: 'https://yourapp.com',  // specific origin (not *)
  credentials: true,               // if using cookies
}));

// Fix 2: Dev proxy — route through same origin (no CORS needed):
// next.config.js:
rewrites: () => [{ source: '/api/:p*', destination: 'http://localhost:8000/api/:p*' }]

// Fix 3: Add OPTIONS handler for preflight:
app.options('*', cors()); // handle preflight for all routes
3

Cause 2 — Mixed Content (HTTP on HTTPS Page)

Mixed content error in console

Mixed Content: The page at 'https://yourapp.com' was loaded over HTTPS, but requested an insecure resource 'http://api.example.com/data'. This request has been blocked; the content must be served over HTTPS.

http:// API URL on https:// page

❌ Bad
// Your page is HTTPS but API is HTTP → blocked by browser
fetch('http://api.example.com/data')  // ❌ http on https page
  .then(r => r.json())
  // TypeError: Failed to fetch

Upgrade API to HTTPS

✅ Good
// Fix 1: Use HTTPS for your API
fetch('https://api.example.com/data')  // ✅

// Fix 2: Use environment variable (set to https:// in production)
const API = process.env.NEXT_PUBLIC_API_URL; // "https://api.example.com"
fetch(`${API}/data`)

// Fix 3: If you can't upgrade the API to HTTPS, use a reverse proxy
// Put Nginx/Cloudflare in front to terminate SSL
4

Cause 3 — Network / DNS Failure

The server is unreachable — wrong URL, server down, DNS not resolving, or user is offline. The request never makes it to the server.

javascriptHandle network failures and offline state gracefully
// Detect which type of network error occurred
async function fetchWithDiagnosis(url, options = {}) {
  try {
    const res = await fetch(url, options);
    return res;
  } catch (err) {
    if (!navigator.onLine) {
      throw new Error('You are offline. Please check your internet connection.');
    }

    // Check if it's a DNS failure vs. server down
    if (err.message.includes('ERR_NAME_NOT_RESOLVED')) {
      throw new Error(`Cannot resolve hostname: ${new URL(url).hostname}`);
    }
    if (err.message.includes('ERR_CONNECTION_REFUSED')) {
      throw new Error(`Server is not running at: ${url}`);
    }

    throw err; // Re-throw unknown errors
  }
}

// Retry with exponential backoff:
async function fetchWithRetry(url, options = {}, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      return await fetch(url, options);
    } catch (err) {
      if (i === retries - 1) throw err;
      await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i))); // 1s, 2s, 4s
      console.log(`Retry ${i + 1}/${retries} after network error...`);
    }
  }
}

// Listen for online/offline events:
window.addEventListener('offline', () => showToast('You are offline.'));
window.addEventListener('online', () => showToast('Back online — retrying...'));
5

Cause 4 — Request Cancelled by AbortController

AbortError indistinguishable from network errors

❌ Bad
// Timeout set too short — request aborted before completing
const controller = new AbortController();
setTimeout(() => controller.abort(), 100); // 100ms is too short!

fetch('/api/slow-endpoint', { signal: controller.signal })
  .catch(e => console.error(e)); // AbortError treated same as "Failed to fetch"

Check err.name === 'AbortError' separately

✅ Good
// Handle AbortError separately from network errors
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10_000); // 10 seconds

fetch('/api/endpoint', { signal: controller.signal })
  .then(r => r.json())
  .catch(err => {
    if (err.name === 'AbortError') {
      showError('Request timed out after 10 seconds. Please try again.');
      return null;  // handle gracefully
    }
    throw err;  // re-throw non-abort errors
  })
  .finally(() => clearTimeout(timeoutId));
6

Cause 5 — SSL Certificate Error

Self-signed or expired SSL certificates cause browsers to block fetch requests. This is common in local development and staging environments with self-signed certificates.

bashFix SSL certificate for local development
# Option 1: mkcert — create trusted local certificates (recommended)
# macOS:
brew install mkcert
mkcert -install          # install local CA into system trust store
mkcert localhost 127.0.0.1  # generate cert trusted by your browser

# Use the cert in your dev server (e.g., Next.js):
# NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem" next dev

# Option 2: Use HTTP in local dev only (add HTTPS in production)
# .env.local:
# NEXT_PUBLIC_API_URL=http://localhost:8000
# .env.production:
# NEXT_PUBLIC_API_URL=https://api.yourdomain.com

# Option 3: In Node.js server-side fetch only (NEVER in browser/production):
# process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';  # ⚠️ insecure!
7

Cause 6 — Browser Extension or Firewall Blocking

Quick test: incognito mode

Open an incognito/private window (disables all extensions) and retry the request. If it works in incognito but fails in your normal window — a browser extension is the cause. Common culprits: uBlock Origin, Privacy Badger, CORS Unblock, VPN extensions, and corporate security proxies.

Ad blocker blocking API domain

Tracking protection lists sometimes block analytics APIs, A/B testing endpoints, or any URL containing "track", "analytics", or "pixel". Whitelist your domain in the extension settings.

VPN extension

VPN extensions can block or reroute requests to certain IPs. Some corporate VPNs block outbound requests to non-approved domains.

CORS Unblock / Allow CORS extension

Ironically, "CORS Unblock" extensions sometimes cause "Failed to fetch" by modifying request headers in a way that confuses the server. Disable it and fix CORS properly on the server.

Browser security policies

Chrome's COEP/COOP headers or Firefox security settings can block certain cross-origin requests. Check for "ERR_BLOCKED_BY_RESPONSE" in the Network tab.

8

Quick Diagnostic Reference

ItemSymptom in Network TabLikely Cause and Fix
No entry at allRequest was never sentCode error (conditional guard), or extension blocking before network
Status: (failed), no CORS msgNetwork/SSL/DNS errorServer down, wrong URL, cert error — try curl to confirm
Status: (failed) + CORS in consoleCORS policy blockAdd Access-Control-Allow-Origin header on server
Mixed content error in consoleHTTP API on HTTPS pageChange API URL to https://
Works in curl, fails in browserBrowser security policyCORS, mixed content, or extension — check each in order
Works in incognito, fails normallyBrowser extension blockingIdentify and disable extensions one by one
AbortError in catch blockRequest cancelled by timeoutIncrease timeout or check if component unmounted

Frequently Asked Questions