Font Converter

Fix Font Not Loading Issues: Complete Troubleshooting Guide

Comprehensive guide to diagnosing and fixing fonts that fail to load. Resolve CORS errors, path issues, format problems, and CSS syntax errors step by step.

TL;DR

In Simple Terms

Step 1: Open DevTools → Network tab → filter by Font. Check for 404 (wrong path), CORS errors (add Access-Control-Allow-Origin header), or no request (CSS syntax error).Common fixes: Verify font file paths are correct and case-sensitive, add format() hints to @font-face src, ensure WOFF2 files aren't corrupted.Still not working? Clear browser cache, check server MIME types (font/woff2 for .woff2), and test in incognito mode to rule out extensions.

Share this page to:

Font loading failures are one of the most frustrating web development issues, causing text to display in fallback fonts or remain invisible until fonts load. When fonts fail to load, users see either a Flash of Unstyled Text (FOUT) with system fonts, or worse, a Flash of Invisible Text (FOIT) where content disappears entirely. These issues damage user experience, harm brand consistency, and can even make content unreadable.

Font loading problems stem from various sources: Cross-Origin Resource Sharing (CORS) restrictions blocking font downloads, incorrect file paths pointing to non-existent resources, unsupported font formats for specific browsers, malformed CSS syntax in @font-face declarations, and caching issues serving stale or corrupted files. Each cause requires a different diagnostic approach and solution.

This guide provides systematic troubleshooting for font loading failures. You'll learn to use browser DevTools to identify the root cause, fix CORS configuration on servers and CDNs, correct path and URL issues, ensure format compatibility across browsers, debug CSS syntax errors, and resolve caching problems. By following these diagnostic steps and solutions, you'll be able to quickly identify and fix any font loading issue.

Understanding Font Loading Issues

How Font Loading Works

Normal Font Loading Process:

  1. Browser parses HTML and encounters text with font-family
  2. Browser checks if font is available locally (system fonts)
  3. If not local, browser looks for @font-face declaration in CSS
  4. Browser downloads font file from specified URL
  5. Browser validates font file format and CORS permissions
  6. Browser applies font to text elements
  7. Text renders with custom font

When Loading Fails:

  • CORS Error: Font file blocked due to cross-origin restrictions
  • 404 Not Found: Font file doesn't exist at specified path
  • Format Error: Browser doesn't support the font format
  • CSS Syntax Error: @font-face declaration has invalid syntax
  • Network Error: Timeout, slow connection, or CDN issues
  • Cache Problem: Browser serving corrupted or outdated cached file

Visible Symptoms:

  • • Text displays in fallback font (Arial, Times New Roman)
  • • Text invisible for 3 seconds, then appears in fallback
  • • Some fonts load, others don't (mixed success)
  • • Fonts work locally but fail in production
  • • Fonts work in one browser but not others
  • • Intermittent loading (works sometimes, fails others)

Key Diagnostic Tool: Browser DevTools

The Network tab in browser DevTools (F12) is your primary diagnostic tool. It shows:

  • • Whether font files are requested
  • • HTTP status codes (200 OK, 404 Not Found, etc.)
  • • CORS error messages
  • • File sizes and load times
  • • Response headers and MIME types

Common Causes

Cause 1: CORS (Cross-Origin Resource Sharing) Errors

Typical Error Message:

Access to font at 'https://cdn.example.com/font.woff2' from origin 
'https://yoursite.com' has been blocked by CORS policy: No 
'Access-Control-Allow-Origin' header is present on the requested resource.

Why This Happens:

  • • Font hosted on different domain/subdomain than website
  • • Server doesn't send Access-Control-Allow-Origin header
  • • Missing crossorigin attribute on preload links
  • • CDN CORS settings not configured
  • Note: CORS is required for ALL web fonts, even same-origin

Cause 2: Incorrect File Paths

Typical Error Message:

Failed to load resource: the server responded with a status of 404 (Not Found)
GET https://yoursite.com/fonts/myfont.woff2 404

Common Path Mistakes:

  • • Relative paths incorrect (../fonts/ vs ./fonts/)
  • • Case sensitivity issues (Font.woff2 vs font.woff2)
  • • Wrong root directory (public/ folder not considered)
  • • File extension typos (.woff vs .woff2)
  • • Spaces in filenames not URL-encoded
  • • Build process not copying fonts to output directory

Cause 3: Unsupported Font Format

Browser Console Warning:

downloadable font: format not supported
source: https://yoursite.com/fonts/font.woff2

Format Support Issues:

  • • IE11 doesn't support WOFF2 (needs WOFF)
  • • Old Safari versions don't support WOFF2
  • • Only WOFF2 provided, no fallback formats
  • • Wrong format() hint in @font-face
  • • Corrupt or incomplete font file

Cause 4: CSS Syntax Errors

Silent Failures (No Error Message):

CSS syntax errors in @font-face often fail silently - the browser ignores the entire declaration without warning.

Common Syntax Mistakes:

  • • Missing quotes around font-family name
  • • Incorrect src: syntax (missing url() or format())
  • • Missing semicolons
  • • Wrong font-weight value (e.g., "bold" instead of 700)
  • • Duplicate @font-face declarations conflicting
  • • CSS comments breaking syntax (/* */ in wrong place)

Cause 5: Incorrect MIME Type

Browser Console Warning:

Resource interpreted as Font but transferred with MIME type text/html
or application/octet-stream

MIME Type Issues:

  • • Server not configured to send correct Content-Type header
  • • WOFF2 served as application/octet-stream instead of font/woff2
  • • WOFF served as text/plain instead of font/woff
  • • Some browsers reject fonts with wrong MIME type

Cause 6: Cache and CDN Issues

Caching Problems:

  • • Browser cache serving old/corrupt font file
  • • CDN cache not invalidated after font update
  • • Service worker caching outdated fonts
  • • Proxy or firewall modifying font files
  • • Mixed HTTP/HTTPS causing cache confusion

Diagnostic Steps

Step-by-Step Diagnostic Process

  1. Open Browser DevTools (F12)
    • Press F12 or right-click → Inspect
    • Go to "Network" tab
    • Filter by "Font" or "All"
    • Refresh page (Ctrl+R or Cmd+R)
  2. Check if Font Requests Are Made
    • Look for .woff2, .woff, .ttf, .otf files in Network tab
    • If NO requests appear: CSS syntax error or font-family not applied
    • If requests appear: Check their status codes
  3. Examine Status Codes
    • 200 OK: Font loaded successfully (check format/CSS if still not working)
    • 404 Not Found: Path issue - file doesn't exist
    • 403 Forbidden: Permission issue or CORS
    • (failed): CORS error or network issue
    • 304 Not Modified: Cached version used
  4. Check Console for Errors
    • Switch to "Console" tab in DevTools
    • Look for red error messages mentioning fonts or CORS
    • Look for orange warnings about MIME types or formats
    • Copy error messages for searching solutions
  5. Inspect Response Headers
    • Click on font file in Network tab
    • Go to "Headers" section
    • Check "Response Headers" for:
    • - Access-Control-Allow-Origin
    • - Content-Type (should be font/woff2, font/woff, etc.)
    • - Cache-Control
  6. Verify Font File Integrity
    • Click on font file in Network tab
    • Check "Size" - should be reasonable (20-200KB typically)
    • If 0 bytes: File empty or redirect issue
    • Try opening font URL directly in browser

Quick Diagnostic Checklist

  • Font files present in Network tab?
    → If NO: CSS syntax error or font-family not used
  • Status code 200 OK?
    → If NO: Check specific error (404, 403, CORS)
  • CORS errors in console?
    → If YES: Fix server headers + add crossorigin attribute
  • Correct Content-Type header?
    → Should be font/woff2, font/woff, etc.
  • File size reasonable?
    → If 0 bytes or very large: Corruption or wrong file
  • Works with cache disabled?
    → If YES when cache disabled: Cache issue

Fixing CORS Issues

Solution 1: Configure Server CORS Headers

For: Self-hosted fonts on your own server

Apache (.htaccess):

# Add to .htaccess in fonts directory or root
<FilesMatch "\.(woff2|woff|ttf|otf|eot)$">
  Header set Access-Control-Allow-Origin "*"
</FilesMatch>

# Or allow specific domain only:
<FilesMatch "\.(woff2|woff|ttf|otf|eot)$">
  Header set Access-Control-Allow-Origin "https://yoursite.com"
</FilesMatch>

Nginx (nginx.conf):

# Add to server block
location ~* \.(woff2|woff|ttf|otf|eot)$ {
  add_header Access-Control-Allow-Origin "*";
}

# Or for specific domain:
location ~* \.(woff2|woff|ttf|otf|eot)$ {
  add_header Access-Control-Allow-Origin "https://yoursite.com";
}

Node.js / Express:

const express = require('express');
const app = express();

// Allow CORS for all font files
app.use('/fonts', (req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  next();
});

// Or use cors middleware
const cors = require('cors');
app.use('/fonts', cors());

Solution 2: Add crossorigin Attribute

Required for: Font preloading and cross-origin fonts

<!-- CRITICAL: crossorigin required even for same-origin fonts -->
<link rel="preload" 
      href="/fonts/myfont.woff2" 
      as="font" 
      type="font/woff2" 
      crossorigin>

<!-- For fonts from CDN -->
<link rel="preload" 
      href="https://cdn.example.com/fonts/font.woff2" 
      as="font" 
      type="font/woff2" 
      crossorigin="anonymous">

<!-- Without crossorigin, fonts won't load! -->

Important: The crossorigin attribute is required for ALL font preloads, even same-origin fonts, due to how browsers handle font requests.

Solution 3: CDN CORS Configuration

For: Fonts hosted on CDNs (Cloudflare, AWS CloudFront, etc.)

Cloudflare:

  • 1. Go to Cloudflare Dashboard → Rules → Transform Rules
  • 2. Create "HTTP Response Header Modification" rule
  • 3. If URI Path matches: /fonts/*
  • 4. Set response header: Access-Control-Allow-Origin: *

AWS S3 Bucket CORS:

[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["GET"],
    "AllowedOrigins": ["*"],
    "ExposeHeaders": []
  }
]

Testing CORS Configuration

# Test CORS headers with curl
curl -I -H "Origin: https://yoursite.com" https://cdn.example.com/fonts/font.woff2

# Look for this in response:
# Access-Control-Allow-Origin: *
# or
# Access-Control-Allow-Origin: https://yoursite.com

# If missing, CORS not configured correctly

Resolving Path Problems

Understanding Path Types

/* Absolute path (from domain root) */
@font-face {
  src: url('/fonts/myfont.woff2');  
  /* Works from: https://yoursite.com/fonts/myfont.woff2 */
}

/* Relative path (from CSS file location) */
@font-face {
  src: url('../fonts/myfont.woff2');  
  /* If CSS in /css/style.css, looks for /fonts/myfont.woff2 */
}

/* Full URL */
@font-face {
  src: url('https://yoursite.com/fonts/myfont.woff2');
  /* Explicit full URL */
}

Common Path Fixes

Fix 1: Use Absolute Paths

/* BEFORE (problematic relative path) */
@font-face {
  font-family: 'MyFont';
  src: url('../fonts/myfont.woff2');  /* May break if CSS location changes */
}

/* AFTER (reliable absolute path) */
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.woff2');  /* Always works from domain root */
}

Fix 2: Check Case Sensitivity

Linux servers are case-sensitive. Windows is not. This causes issues when deploying.

/* File named: MyFont.woff2 */

/* WRONG (case mismatch) */
src: url('/fonts/myfont.woff2');  /* Fails on Linux */

/* CORRECT (exact match) */
src: url('/fonts/MyFont.woff2');  /* Works everywhere */

/* BEST PRACTICE: Use lowercase filenames */
/* Rename file to: myfont.woff2 */
src: url('/fonts/myfont.woff2');  /* No case issues */

Fix 3: URL-Encode Spaces and Special Characters

/* WRONG (spaces in filename) */
src: url('/fonts/My Font.woff2');  /* Breaks! */

/* CORRECT (URL-encoded) */
src: url('/fonts/My%20Font.woff2');  /* Works */

/* BEST PRACTICE: No spaces in filenames */
/* Rename to: my-font.woff2 or myfont.woff2 */
src: url('/fonts/my-font.woff2');  /* Best approach */

Framework-Specific Path Issues

Next.js:

/* Fonts go in /public/fonts/ directory */
/* In CSS, reference from root: */
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.woff2');
  /* NOT /public/fonts/myfont.woff2 */
}

React (Create React App):

/* Fonts go in /public/fonts/ directory */
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.woff2');
  /* or use %PUBLIC_URL% in HTML */
  /* src: url('%PUBLIC_URL%/fonts/myfont.woff2'); */
}

Vue.js:

/* Fonts in /public/fonts/ */
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.woff2');
}

/* Or fonts in /src/assets/fonts/ (webpack will process) */
@font-face {
  font-family: 'MyFont';
  src: url('~@/assets/fonts/myfont.woff2');
}

Verifying Correct Paths

  1. Check Network Tab URL:

    Look at the full URL the browser is requesting. Is it correct?

  2. Try Opening Font URL Directly:

    Copy the font URL from Network tab, paste in browser address bar. Does it download?

  3. Verify File Exists on Server:

    SSH into server or check file manager. Confirm file is actually there with correct name.

Format Compatibility

FormatBrowser SupportWhen to Use
WOFF2Chrome 36+, Firefox 39+, Edge 14+, Safari 10+, Opera 23+
~97% of users
✓ Primary format
✓ Best compression
✓ Modern sites
WOFFIE9+, All modern browsers
~99% of users
✓ Fallback for IE11
✓ Old Safari support
✓ Legacy compatibility
TTF/OTFAll browsers (partial IE support)
Universal
✗ Avoid for web (no compression)
✓ Source files only
EOTIE6-8 only
<0.5% of users
✗ Obsolete
Only if supporting IE8

Recommended Format Stack

Modern Sites (97% coverage):

@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.woff2') format('woff2');
  /* WOFF2 only - fast, small, modern */
}

With IE11 Support (99% coverage):

@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.woff2') format('woff2'),
       url('/fonts/myfont.woff') format('woff');
  /* Browsers use first supported format */
}

Maximum Compatibility (including old browsers):

@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.woff2') format('woff2'),
       url('/fonts/myfont.woff') format('woff'),
       url('/fonts/myfont.ttf') format('truetype');
  /* Covers virtually all browsers */
}

Common Format Mistakes

/* WRONG: Missing format() hint */
@font-face {
  src: url('/fonts/myfont.woff2');  /* Browser may not recognize format */
}

/* CORRECT: Include format() */
@font-face {
  src: url('/fonts/myfont.woff2') format('woff2');
}

/* WRONG: Wrong format hint */
@font-face {
  src: url('/fonts/myfont.woff2') format('woff');  /* Incorrect! */
}

/* CORRECT: Matching format */
@font-face {
  src: url('/fonts/myfont.woff2') format('woff2');
}

CSS Syntax Errors

Common CSS Syntax Mistakes

Mistake 1: Missing Quotes

/* WRONG */
@font-face {
  font-family: My Font;  /* Missing quotes - breaks! */
}

/* CORRECT */
@font-face {
  font-family: 'My Font';  /* Quotes required for names with spaces */
}

Mistake 2: Wrong font-weight Values

/* WRONG */
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/bold.woff2') format('woff2');
  font-weight: bold;  /* String "bold" is invalid! */
}

/* CORRECT */
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/bold.woff2') format('woff2');
  font-weight: 700;  /* Numeric value required */
}

Mistake 3: Missing Semicolons

/* WRONG */
@font-face {
  font-family: 'MyFont'  /* Missing semicolon */
  src: url('/fonts/font.woff2') format('woff2')  /* Missing semicolon */
}

/* CORRECT */
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/font.woff2') format('woff2');
}

Mistake 4: Incorrect src Syntax

/* WRONG: Missing url() wrapper */
@font-face {
  font-family: 'MyFont';
  src: '/fonts/font.woff2';  /* Breaks! */
}

/* WRONG: Missing quotes in url() */
@font-face {
  src: url(/fonts/font.woff2) format('woff2');  /* May fail */
}

/* CORRECT */
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/font.woff2') format('woff2');
}

Correct @font-face Template

/* Perfect @font-face declaration */
@font-face {
  font-family: 'MyFont';  /* Quotes required */
  src: url('/fonts/myfont.woff2') format('woff2'),  /* url() + format() */
       url('/fonts/myfont.woff') format('woff');    /* Fallback */
  font-weight: 400;        /* Numeric value */
  font-style: normal;      /* normal or italic */
  font-display: swap;      /* Optional but recommended */
}

/* Using the font */
body {
  font-family: 'MyFont', Arial, sans-serif;  /* Quotes match @font-face */
}

Validating CSS Syntax

  1. Use CSS Validator:

    https://jigsaw.w3.org/css-validator/ - Paste your CSS to check for errors

  2. Check Browser Console:

    CSS parsing errors appear in Console tab (though font-face errors often silent)

  3. Use VS Code Extensions:

    "CSS Lint" extension highlights syntax errors as you type

Cache and CDN Issues

Clearing Browser Cache

Hard Refresh:

  • Windows/Linux: Ctrl + Shift + R or Ctrl + F5
  • Mac: Cmd + Shift + R
  • • Bypasses cache and reloads all resources

Disable Cache in DevTools:

  1. Open DevTools (F12)
  2. Go to Network tab
  3. Check "Disable cache" checkbox
  4. Keep DevTools open
  5. Refresh page

Clear Specific Site Cache:

  • • Chrome: Settings → Privacy → Clear browsing data → Cached images and files
  • • Firefox: History → Clear Recent History → Cache
  • • Safari: Develop → Empty Caches

Clearing CDN Cache

Cloudflare:

  1. Go to Cloudflare Dashboard
  2. Select your domain
  3. Go to Caching → Configuration
  4. Click "Purge Everything" or "Custom Purge"
  5. Enter font file URLs if using custom purge

AWS CloudFront:

# Create invalidation
aws cloudfront create-invalidation \
  --distribution-id DISTRIBUTION_ID \
  --paths "/fonts/*"

# Or invalidate specific file
aws cloudfront create-invalidation \
  --distribution-id DISTRIBUTION_ID \
  --paths "/fonts/myfont.woff2"

Generic CDN Cache Busting:

/* Add version query parameter to force reload */
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.woff2?v=2') format('woff2');
  /* Change ?v=2 to ?v=3 when you update font */
}

Service Worker Cache Issues

Check for Service Worker:

  1. Open DevTools → Application tab
  2. Look at "Service Workers" section
  3. If service worker exists, click "Unregister"
  4. Click "Clear storage" to remove cached fonts
  5. Refresh page

Update Service Worker to Not Cache Fonts:

// In service-worker.js
self.addEventListener('fetch', event => {
  // Don't cache font files
  if (event.request.url.includes('/fonts/')) {
    return fetch(event.request);
  }
  // Cache other resources...
});

Prevention Best Practices

Checklist for Reliable Font Loading

  • Use correct @font-face syntax
    • • Quotes around font-family
    • • url() wrapper for paths
    • • format() hints included
    • • Numeric font-weight values
  • Configure CORS headers
    • • Access-Control-Allow-Origin: * on server
    • • crossorigin attribute on preload links
    • • CDN CORS settings enabled
  • Use absolute paths
    • • Start with / for domain root paths
    • • Avoid relative paths (../)
    • • Use lowercase filenames (no spaces)
  • Provide format fallbacks
    • • WOFF2 primary (modern browsers)
    • • WOFF fallback (IE11, old Safari)
    • • Both formats tested
  • Set correct MIME types
    • • font/woff2 for .woff2 files
    • • font/woff for .woff files
    • • Server configured correctly
  • Test across browsers
    • • Chrome, Firefox, Safari, Edge
    • • Mobile browsers (iOS Safari, Chrome Mobile)
    • • Use BrowserStack if needed
  • Monitor in production
    • • Check Network tab after deployment
    • • Test with cleared cache
    • • Verify CDN serving correctly

Production-Ready Font Loading Template

<!-- In HTML <head> -->
<link rel="preload" 
      href="/fonts/myfont-regular.woff2" 
      as="font" 
      type="font/woff2" 
      crossorigin>

<!-- In CSS -->
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont-regular.woff2') format('woff2'),
       url('/fonts/myfont-regular.woff') format('woff');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont-bold.woff2') format('woff2'),
       url('/fonts/myfont-bold.woff') format('woff');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

body {
  font-family: 'MyFont', -apple-system, BlinkMacSystemFont, 
               'Segoe UI', Arial, sans-serif;
}

/* Server configuration (Apache .htaccess) */
<FilesMatch "\.(woff2|woff|ttf|otf)$">
  Header set Access-Control-Allow-Origin "*"
  Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>

Summary: Fixing Font Loading Issues

Font loading failures typically stem from CORS misconfigurations, incorrect file paths, unsupported formats, or CSS syntax errors. Use browser DevTools Network tab to diagnose the specific issue, then apply the appropriate fix: configure CORS headers for cross-origin fonts, use absolute paths for reliability, provide WOFF2 + WOFF formats for compatibility, and validate CSS syntax carefully.

Prevention is straightforward: use the production-ready template above, test across browsers before deploying, and always check DevTools after deployment to verify fonts load correctly. When issues occur, follow the diagnostic checklist to quickly identify and resolve the root cause.

Sarah Mitchell

Written by

Sarah Mitchell

Product Designer, Font Specialist

Marcus Rodriguez

Verified by

Marcus Rodriguez

Lead Developer