cache-headers
Skill6 starsChanged 12 days agoMITView on GitHub
Cache architecture for SEO: Cache-Control, CDN vs browser cache, ETag and Last-Modified, and CMS setups (WP Rocket, LiteSpeed, PrestaShop, Nginx/Apache). Use for TTFB issues, caching layers or cache header audits.
- Sends data out
In this repository40 other files it gives its agents.
- accesibilidad-wcagSkill
- canonicalSkill
- ccpaSkill
- core-web-vitalsSkill
- ga4-ai-trafficSkill
- ga4-analysisSkill
- geo-ai-discoverabilitySkill
- google-tag-managerSkill
- hreflangSkill
- image-optimizationSkill
- javascript-seoSkill
- ley-25326Skill
- ley-datos-chileSkill
- lfpdpppSkill
- lgpdSkill
- mineria-de-impresionesSkill
- nfadpSkill
- on-page-fundamentalsSkill
- prestashop-seoSkill
- privacidadSkill
- redirectsSkill
- rgpdSkill
- robots-txtSkill
- schema-markupSkill
- screaming-frogSkill
- semrushSkill
- seo-educacionSkill
- seo-gsc-diagnosticsSkill
- seo-interlinkingSkill
- seo-negativoSkill
- seo-quoteSkill
- se-rankingSkill
- sitemapSkill
- ssl-httpsSkill
- third-party-scriptsSkill
- uk-gdprSkill
- ux-heuristicaSkill
- wordpress-diviSkill
- wordpress-elementorSkill
- wordpress-hidden-errorsSkill
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 2 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 163
Verify this is working: publish a post and check the response headers for the page — X-LiteSpeed-Cache: miss confirms a fresh request was …
- Distinctive: line 328
When you publish new content, the CDN must serve the new version.
- Distinctive: line 454
Check CDN rules — HTML may need an explicit "Cache Everything" rule.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Cache Headers & Caching Strategy — Technical SEO
Correct caching is the single highest-ROI performance optimization for most sites. A cold origin TTFB of 800ms becomes < 50ms with a CDN cache hit.
Cache-Control directives reference
| Directive | Meaning | Use case |
max-age=N | Browser can cache for N seconds | Static assets |
s-maxage=N | CDN/shared cache can cache for N seconds | CDN-specific TTL |
no-cache | Must revalidate with server before using cache | HTML pages |
no-store | Do not cache at all | Authenticated/transactional pages |
must-revalidate | Once stale, must revalidate (do not serve stale) | HTML pages |
public | Shared caches (CDN) can store this response | Static assets, HTML |
private | Only browser cache, not CDN | Logged-in user pages |
immutable | Content will not change — no revalidation needed | Versioned static assets |
stale-while-revalidate=N | Serve stale while fetching fresh in background | HTML for fast TTFB |
Combinations by content type
Static assets (CSS, JS, images) — versioned with hash in filename:
Cache-Control: public, max-age=31536000, immutable
HTML pages (anonymous visitors):
Cache-Control: public, max-age=0, must-revalidate
or with CDN:
Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400
HTML pages (logged-in users):
Cache-Control: private, no-cache
Checkout / cart / account:
Cache-Control: no-store
API responses (public):
Cache-Control: public, max-age=300, s-maxage=600
Sitemaps and robots.txt:
Cache-Control: public, max-age=3600Cache layers architecture
User browser
↓
Browser cache (private, per user)
↓
CDN edge (shared, per PoP — Cloudflare, Fastly, CloudFront, etc.)
↓
Reverse proxy / load balancer (Nginx, Varnish)
↓
Application cache (WP Rocket page cache, LiteSpeed Cache, PrestaShop CCC)
↓
Object cache (Redis, Memcached — DB query results)
↓
PHP runtime (OPcache — compiled PHP bytecode)
↓
Database (MySQL / MariaDB)Each layer reduces load on the next. A CDN hit eliminates all layers below it. A page cache hit eliminates the PHP and database layers.
ETag and Last-Modified
Both are validation mechanisms — they allow a client with a cached response to ask "has this changed?" without downloading the full response again.
Last-Modified:
Response: Last-Modified: Wed, 16 Apr 2026 10:00:00 GMT
Next request: If-Modified-Since: Wed, 16 Apr 2026 10:00:00 GMT
Server: 304 Not Modified (if unchanged) or 200 + new contentETag:
Response: ETag: "abc123"
Next request: If-None-Match: "abc123"
Server: 304 Not Modified (if same) or 200 + new contentSEO relevance: ETags and Last-Modified enable Googlebot to efficiently re-crawl pages — if the content has not changed, Googlebot receives 304 and skips re-processing. This conserves crawl budget on large sites.
When ETags are a problem: Nginx and Apache can generate ETags that include inode numbers. On multi-server setups, different servers generate different ETags for the same file → cache validation fails → unnecessary full responses.
Fix for Nginx:
etag off; # or use FileETag MTime Size in ApacheWordPress caching
Page cache — plugin options
| Plugin | Mechanism | Recommended stack |
| WP Rocket | File-based page cache | Nginx or Apache + LiteSpeed |
| LiteSpeed Cache | LiteSpeed server-native | Hostinger, LiteSpeed servers |
| W3 Total Cache | Multiple backends | VPS with Redis/Memcached |
| WP Super Cache | File-based | Basic shared hosting |
WP Rocket configuration
Page Cache:
- Settings > Cache > Enable caching for mobile devices: on
- Separate cache for mobile: on (if theme is responsive, keep off)
- Cache lifespan: 10 hours (adjust to content update frequency)
Browser cache:
- Settings > Browser Caching > on
- WP Rocket handles Cache-Control headers for static assets
CDN integration:
- Settings > CDN > CDN hostname: your CDN URL
- This rewrites asset URLs to the CDN hostname
Exclusions (important for e-commerce):
URLs to never cache:
/cart/
/checkout/
/my-account/
/order-received/
Cookies that bypass cache:
woocommerce_cart_hash
woocommerce_items_in_cart
wp_woocommerce_session_LiteSpeed Cache
Common misconfiguration — X-Robots-Tag on XML files:
LiteSpeed may apply X-Robots-Tag: noindex to all XML responses including sitemaps.
Fix: LiteSpeed Cache > Advanced > Excludes > Do Not Cache URIs > /sitemap*.xml
Cache purge on publish:
LiteSpeed Cache automatically purges page cache when a post is published or updated.
Verify this is working: publish a post and check the response headers for the page —
X-LiteSpeed-Cache: miss confirms a fresh request was served.
Separate mobile cache — WP Customizer CSS stripped:
LiteSpeed Cache can maintain separate cached versions for desktop and mobile (detected
by User-Agent). When CSS is added via WP Customizer (Additional CSS), LiteSpeed's CSS
optimization strips the <style id="wp-custom-css"> block when building the mobile
cache. Result: CSS works for logged-in admins (who bypass cache) and in DevTools mobile
simulation (which uses the desktop cache), but not on real mobile devices.
Diagnosis:
# Fetch with mobile UA and check for wp-custom-css block
curl -s -A "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 Mobile/15E148 Safari/604.1" \
https://domain.com/page/ | grep -i "wp-custom-css\|font-size"
# If wp-custom-css is absent from the HTML, LiteSpeed is stripping itFix — use Code Snippets plugin (scope site-css) instead of WP Customizer:
Code Snippets generates a dedicated external stylesheet (?code-snippets-css=1) that
LiteSpeed includes correctly in the HTML for all devices. The WP Customizer inline
<style> block is unreliable with LiteSpeed CSS optimization enabled.
# Create CSS snippet via Code Snippets REST API
curl -X POST https://domain.com/wp-json/code-snippets/v1/snippets \
-H "Authorization: Basic <base64(user:app-password)>" \
-H "Content-Type: application/json" \
-d '{"title":"my-css","code":".selector { property: value !important; }","scope":"site-css","active":true}'
# Then purge LiteSpeed cache manually from WP AdminNote: WP Admin may be blocked by Cloudflare for scripted requests. In that case the cache purge must be triggered manually from the WordPress dashboard.
Redis object cache
Object cache stores database query results in memory. Eliminates repeated identical queries to MySQL.
// wp-config.php
define('WP_CACHE_KEY_SALT', 'unique-site-key');
define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);Object cache reduces TTFB on pages with complex WP_Query calls (WooCommerce shop, custom post type archives, widgets pulling recent posts).
PrestaShop caching
CCC (Combine, Compress, Cache)
Path: Advanced Parameters > Performance > CCC
| Option | Effect | Recommended |
| Smart cache for CSS | Combines and compresses CSS | On |
| Smart cache for JavaScript | Combines and compresses JS | On |
| Minify HTML | Removes whitespace from HTML | On |
| Compress inline JavaScript | Minifies inline JS | On |
| Move JavaScript to end | Defers JS to before </body> | On |
Warning: CCC can break custom themes. Test on staging first.
Smarty cache
Path: Advanced Parameters > Performance > Smarty
| Setting | Production value |
| Cache compilation | Never recompile template files |
| Cache | Enabled |
| Caching type | File system |
Full-page cache for PrestaShop
PrestaShop does not include a built-in full-page cache. Options:
- Varnish in front of the application server — caches full HTML responses
- Nginx FastCGI cache — caches PHP-FPM responses at the web server level
- Third-party module — available on PrestaShop Addons marketplace
Configure cache bypass for:
/order/(checkout)/cart/- Any URL with
PHPSESSIDcookie set
Nginx caching configuration
FastCGI page cache
# In http block
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=WORDPRESS:100m
inactive=60m max_size=1g;
# In server block
set $skip_cache 0;
# Bypass cache for logged-in users and WooCommerce
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|woocommerce_cart_hash|woocommerce_items_in_cart") {
set $skip_cache 1;
}
if ($request_uri ~* "/wp-admin/|/wp-login.php|/cart/|/checkout/|/my-account/") {
set $skip_cache 1;
}
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 301 302 1h;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-FastCGI-Cache $upstream_cache_status;Static asset cache headers
# CSS, JS, fonts — long cache with versioning assumption
location ~* \.(css|js|woff2|woff|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# Images
location ~* \.(jpg|jpeg|png|gif|webp|avif|svg|ico)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000";
}
# Sitemaps and feeds — shorter TTL
location ~* \.(xml|txt)$ {
expires 1h;
add_header Cache-Control "public, max-age=3600";
}CDN caching
What CDNs cache by default
Most CDNs (Cloudflare, Fastly, CloudFront) cache:
- CSS, JS, images, fonts — yes, by default
- HTML — no, not by default (treated as dynamic)
To cache HTML, you must configure a cache rule:
Cloudflare — cache HTML with short TTL:
Rule: Cache Everything
Edge TTL: 1 hour
Bypass cache: Cookie contains "wordpress_logged_in" OR URI contains "/wp-admin/"Effect: TTFB drops from 400-800ms (origin) to 10-50ms (CDN edge).
Cache invalidation
When you publish new content, the CDN must serve the new version. Options:
- Short TTL (1h): cache expires automatically. Simple but means stale content for up to 1 hour after a change.
- Cache purge on publish: WordPress plugins (WP Rocket, LiteSpeed) can purge the CDN cache for specific URLs when content is updated.
- Cache-Control: stale-while-revalidate: serve stale while fetching fresh in background — best user experience.
LiteSpeed + Cloudflare — respect_origin no tiene nada que respetar
Combinación muy común (WordPress + LiteSpeed Cache + Cloudflare) con un fallo silencioso que no aparece en ningún panel: el HTML apenas se cachea en el edge y el TTFB de campo se dispara a 2 s o más, mientras que cada petición individual que se prueba a mano parece rápida.
La causa: LiteSpeed Cache emite x-litespeed-cache-control: public,max-age=604800, una
cabecera propietaria que Cloudflare no interpreta, y no emite ningún Cache-Control
estándar. Si la Cache Rule de Cloudflare usa Edge TTL: respect origin, Cloudflare no
encuentra ninguna directiva que respetar y cae a sus heurísticas por defecto, reteniendo el
HTML unos pocos segundos. Cada expiración cuesta un render PHP completo.
Cómo confirmarlo — comparar la respuesta con y sin que aplique la Cache Rule. Enviar una cookie de sesión de WordPress hace que la regla no aplique y devuelve la respuesta del origen tal cual:
# Origen directo (la regla de CF no aplica por la cookie)
curl -sI -H 'Cookie: wordpress_logged_in_x=1' "https://example.com/post/?x=$RANDOM" \
| grep -iE 'cache-control|x-litespeed|cf-cache-status'
# Con la regla de CF aplicando
curl -sI "https://example.com/post/?x=$RANDOM" \
| grep -iE 'cache-control|x-litespeed|cf-cache-status'Diagnóstico según lo que aparezca:
| Observación | Significado |
El origen no devuelve Cache-Control, solo x-litespeed-cache-control | respect_origin es inservible → usar override_origin con TTL explícito |
Cache-Control: no-store aparece solo cuando la regla aplica | Lo genera browser_ttl: bypass de la propia regla. Instruye al navegador, no al edge. No es la causa del problema de caché |
x-litespeed-cache: miss siempre, incluso repitiendo la misma URL | La caché de página de LiteSpeed no sirve nunca (ver abajo) |
El fix es dejar de depender del origen para el TTL del edge:
Cloudflare → Caching → Cache Rules → [tu regla de HTML]
Edge TTL: Override origin → 4 hours
Browser TTL: Bypass (correcto para HTML: el visitante siempre recibe contenido fresco)Vía API, conservando el resto de la regla intacta:
curl -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE/rulesets/$RULESET" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"rules":[{"action":"set_cache_settings","description":"Cache HTML anon",
"expression":"<tu expresion original>","enabled":true,
"action_parameters":{"cache":true,"browser_ttl":{"mode":"bypass"},
"edge_ttl":{"mode":"override_origin","default":14400}}}]}'Exporta primero el ruleset actual (GET .../rulesets/phases/http_request_cache_settings/entrypoint)
como respaldo: el PUT reemplaza el conjunto completo de reglas, no hace merge.
x-litespeed-cache: miss permanente es un problema aparte. Si el servidor responde miss
en todas las peticiones —incluso pidiendo dos veces seguidas la misma URL contra el origen y en
todos los tipos de página— la caché de página de LiteSpeed no está sirviendo, aunque el plugin
esté activo y marque la página como cacheable. La causa habitual es que falten las reglas del
servidor en el .htaccess:
<IfModule LiteSpeed>
CacheLookup on
</IfModule>Requiere acceso al servidor o al hosting; no es diagnosticable ni corregible desde la API de WordPress. Con Cloudflare bien configurado el impacto en usuarios queda absorbido, pero cada purga del CDN vuelve a costar un render PHP completo y el origen queda expuesto si el CDN falla.
Diagnosing cache issues
Check headers directly
# Check what cache headers a URL is returning
curl -I https://domain.com/page/ 2>&1 | grep -i "cache\|x-cache\|age\|etag\|last-modified"Key headers to read:
| Header | Value | Meaning |
Cache-Control: no-store | — | Response will never be cached |
X-Cache: HIT | — | CDN served from cache |
X-Cache: MISS | — | CDN fetched from origin |
Age: 3600 | seconds | Response has been cached for 3600s |
CF-Cache-Status: DYNAMIC | Cloudflare | HTML not being cached |
X-LiteSpeed-Cache: hit | LiteSpeed | Served from LiteSpeed cache |
Common issues
Cache-Control: no-store on all HTML (PrestaShop default)
PrestaShop ships with no-store on all HTML responses to prevent caching of
session-aware pages. Fix: enable CCC and Smarty cache (see PrestaShop section).
Cache-Control: private on anonymous pages
WordPress plugins or themes that set private prevent CDNs from caching pages
that could be safely cached for anonymous visitors.
No cache headers at all
Old PHP applications or misconfigured servers may emit no Cache-Control header. Browsers default to heuristic caching (usually 10% of the Last-Modified age). Explicit headers are always better.
Cache headers correct but TTFB still high
CDN is configured but HTML is bypassing it (CF-Cache-Status: DYNAMIC).
Check CDN rules — HTML may need an explicit "Cache Everything" rule.
Audit checklist
CRITICAL
[ ] HTML pages return Cache-Control header (not absent)
[ ] Cart, checkout, and account pages return Cache-Control: no-store
[ ] No Cache-Control: no-store on anonymous HTML pages
[ ] Sitemap does not return X-Robots-Tag: noindex (LiteSpeed issue)
HIGH
[ ] TTFB < 800ms on primary pages (PSI field data or curl)
[ ] Page caching active (WP Rocket, LiteSpeed, Smarty)
[ ] Static assets (CSS, JS, images) have long max-age (>1 year)
[ ] CDN configured and serving static assets (X-Cache: HIT or CF-Cache-Status: HIT)
MEDIUM
[ ] CDN caching HTML for anonymous visitors (not just assets)
[ ] OPcache active for PHP (check php.ini or phpinfo())
[ ] Redis/Memcached for WordPress object cache (high-traffic sites)
[ ] ETag configuration on multi-server setups (no inode-based ETags)
[ ] PrestaShop: CCC active with all options enabled
LOW
[ ] Cache-Control: immutable on versioned static assets
[ ] stale-while-revalidate configured for HTML responses
[ ] Cache purge on content publish (CDN invalidation)
[ ] Age header present on CDN responses (confirms caching is active)Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

