Favicon Caching Strategy Guide
Complete guide to favicon caching: browser cache behavior, optimal server headers, CDN configuration, and effective cache invalidation strategies.
Why Favicon Caching Matters
Requests eliminated
Load time when cached
Recommended duration
Bandwidth saved/visit
Understanding Cache Levels
1. Browser Cache
Location: User's device
Duration: Based on headers
Control: Cache-Control headers
Impact: Fastest (0ms)
2. CDN Cache
Location: Edge servers
Duration: CDN configuration
Control: CDN settings + headers
Impact: Very fast (10-50ms)
3. Proxy Cache
Location: ISP/corporate
Duration: Varies
Control: public/private directives
Impact: Fast (50-100ms)
Optimal Cache Headers
Recommended Configuration
Perfect Cache Headers for Favicons:
Cache-Control: public, max-age=31536000, immutable
Expires: [Date 1 year from now]
ETag: "abc123def456"| Directive | Value | Purpose |
|---|---|---|
public |
- | Allow caching by browsers and CDNs |
max-age |
31536000 | Cache for 1 year (in seconds) |
immutable |
- | Never revalidate (file won't change) |
Expires |
[Date] | HTTP/1.0 compatibility (backup) |
ETag |
[Hash] | Conditional requests (optional) |
Server Configuration Examples
# Nginx favicon caching
server {
# All favicon files
location ~* \.(ico|png|svg|webmanifest|xml)$ {
# 1 year cache
expires 1y;
# Cache-Control headers
add_header Cache-Control "public, immutable" always;
# Optional: ETag
etag on;
# Don't log these requests
access_log off;
}
# Specific favicon.ico
location = /favicon.ico {
expires 1y;
add_header Cache-Control "public, immutable" always;
log_not_found off;
access_log off;
}
}# Apache .htaccess or httpd.conf
# Enable expires module
<IfModule mod_expires.c>
ExpiresActive On
# Favicon files - 1 year
ExpiresByType image/x-icon "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType application/manifest+json "access plus 1 year"
</IfModule>
# Cache-Control headers
<IfModule mod_headers.c>
<FilesMatch "\.(ico|png|svg|webmanifest)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
</IfModule>
# Optional: Disable ETag to save bandwidth
FileETag None<!-- IIS web.config -->
<configuration>
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge"
cacheControlMaxAge="365.00:00:00" />
</staticContent>
<outboundRules>
<rule name="Add immutable">
<match serverVariable="RESPONSE_Cache-Control"
pattern=".*" />
<conditions>
<add input="{REQUEST_FILENAME}"
pattern="\.(ico|png|svg)$" />
</conditions>
<action type="Rewrite" value="public, max-age=31536000, immutable" />
</rule>
</outboundRules>
</system.webServer>
</configuration>// Express.js favicon caching
const express = require('express');
const app = express();
// Static files with caching
app.use(express.static('public', {
maxAge: '1y', // 1 year
immutable: true,
setHeaders: (res, path) => {
if (path.match(/\.(ico|png|svg|webmanifest)$/)) {
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
}
}
}));
// Or specific route
app.get('/favicon.ico', (req, res) => {
res.set('Cache-Control', 'public, max-age=31536000, immutable');
res.sendFile(__dirname + '/public/favicon.ico');
});CDN Caching Configuration
Cloudflare
Default: Respects origin headers
Edge TTL: 1 year recommended
Page Rule Configuration:
URL: *favicon* OR *.ico OR *.png
Setting: Edge Cache TTL = 1 year
Browser Cache TTL = Respect Existing
Purge cache: Purge Everything or specific URLs
AWS CloudFront
Behavior: Create path pattern
TTL: Min=31536000, Default=31536000, Max=31536000
Path Patterns:
/favicon.ico
/favicon-*.png
/*.svg
/site.webmanifest
Invalidation: Create invalidation for updated files
Browser-Specific Caching Behavior
| Browser | Cache Behavior | Cache Location | Clear Method |
|---|---|---|---|
| Chrome | Aggressive, respects headers | Disk + Memory | Ctrl+Shift+Delete or Ctrl+F5 |
| Firefox | Very persistent (favicons.sqlite) | Database + Disk | Clear history or delete DB file |
| Safari | Aggressive, long retention | Disk cache | Clear history or Cmd+Shift+R |
| Edge | Similar to Chrome | Disk + Memory | Ctrl+Shift+Delete |
| Mobile Safari | Extremely persistent for home screen | Permanent until icon removed | Delete & re-add home screen icon |
Cache Invalidation Strategies
How to Update Cached Favicons
Strategy 1: Versioned URLs (Recommended)
<!-- Add version query parameter -->
<link rel="icon" href="/favicon.ico?v=2">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png?v=2">
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png?v=2">
<link rel="icon" type="image/png" sizes="512x512" href="/favicon-512x512.png?v=2">
<!-- Or change filename -->
<link rel="icon" href="/favicon-v2.ico">
<link rel="apple-touch-icon" href="/apple-touch-icon-v2.png">? Pros:
- Instant cache bypass
- Works for all users immediately
- No server-side changes needed
- CDN-friendly
? Cons:
- Need to update HTML
- Query parameters may be stripped by some proxies
- Old files remain on server
Strategy 2: Cache Purging
# Cloudflare CLI
cf-cli purge https://domain.com/favicon.ico
# AWS CloudFront
aws cloudfront create-invalidation --distribution-id X --paths "/favicon.ico"
- Can't force users to clear
- Hard refresh helps (Ctrl+F5)
- Private browsing sees new version
- Wait for cache expiry
Testing Cache Configuration
Verify Caching is Working
1. Check Headers with curl:
# Check cache headers
curl -I https://yourdomain.com/favicon.ico
# Expected output should include:
# Cache-Control: public, max-age=31536000, immutable
# Expires: [Date 1 year from now]
# Check if gzipped (for SVG)
curl -H "Accept-Encoding: gzip" -I https://yourdomain.com/favicon.svg
# Look for: Content-Encoding: gzip2. Browser DevTools:
- Open DevTools (F12) ? Network tab
- Reload page (F5)
- Find favicon.ico in list
- Check Size column - should show "disk cache" or "memory cache" on second load
- Check Headers tab for Cache-Control
3. Online Tools:
- RedBot: https://redbot.org/ - Cache header analysis
- HTTP Headers: https://httpstatus.io/ - Quick header check
- GTmetrix: https://gtmetrix.com/ - Full performance scan
Common Caching Issues & Solutions
- Add version parameter:
favicon.ico?v=2 - Purge CDN cache if using CDN
- Hard refresh browser: Ctrl+Shift+R or Ctrl+F5
- Clear browser cache completely
- Test in Private/Incognito window
- Wait for cache to expire naturally (up to 1 year!)
- Server module enabled (mod_expires for Apache)
- Configuration syntax correct
- Server restarted after config change
- Not being overridden by application
- Using correct file path/location blocks
- Test with curl to verify headers
Solution:
- Set consistent headers for all browsers
- Use both Cache-Control and Expires
- Add
immutabledirective - Test in all major browsers
- Version URLs for critical updates
Caching Best Practices Summary
? Do This
- Set 1-year cache for favicons
- Use
immutabledirective - Include both Cache-Control and Expires
- Version URLs when updating
- Enable CDN caching
- Test with multiple browsers
- Monitor cache hit rates
- Document cache strategy
? Avoid This
- Short cache durations (< 1 month)
- no-cache or no-store directives
- Relying on ETag only (uses bandwidth)
- Forgetting Expires header
- Not versioning for updates
- Disabling cache in production
- Ignoring CDN cache settings
- Not testing cache behavior
Generate Cache-Optimized Favicons
Our generator includes optimal caching instructions
Generate Favicons