What does Google say about SEO? /
Quick SEO Quiz

Test your SEO knowledge in 5 questions

Less than a minute. Find out how much you really know about Google search.

🕒 ~1 min 🎯 5 questions

Official statement

When the 'max-age' and 'Expires' headers are used together, the 'max-age' header takes priority and overrides 'Expires'. This allows you to set the exact cache duration in seconds.
2:12
🎥 Source video

Extracted from a Google Search Central video

⏱ 2:44 💬 EN 📅 23/06/2009 ✂ 2 statements
Watch on YouTube (2:12) →
Other statements from this video 1
  1. 0:06 Le cache navigateur peut-il vraiment booster votre SEO ?
📅
Official statement from (16 years ago)
TL;DR

Google confirms that the Cache-Control max-age header takes precedence over Expires when both are present. This hierarchy is not negotiable: max-age defines the cache duration in seconds and completely ignores Expires. For SEO, this is an opportunity to take precise control of caching, but also to fix conflicting configurations that sabotage content freshness.

What you need to understand

Why does Google emphasize this hierarchy between max-age and Expires?

Both headers exist to tell browsers and CDNs how long they can keep a resource cached. Expires uses an absolute date/time ("Expires: Thu, 01 Dec 2025 16:00:00 GMT"), whereas max-age counts in seconds from the time the response is served ("Cache-Control: max-age=3600"). When both coexist, it creates confusion: which directive should be respected?

Google is clear. Max-age always wins. Regardless of what Expires states, if max-age is present, it dictates the duration. This priority simplifies the lives of crawlers and browsers, but can also create pitfalls for those who do not check their headers.

What impact does this have on crawling and indexing?

Googlebot respects cache directives just like any modern HTTP client. If max-age says "cache this for 7 days," the bot probably won't return before that. If you have an outdated Expires floating around with a short max-age, it's max-age that counts: no surprises there.

The problem arises when you think you're relying on Expires while max-age nullifies everything. You believe you've forced a long expiration, but a hidden max-age=0 in your Apache or Nginx configuration overrides your directive. Result: pages recrawled too frequently, server overload, or worse, stale content because you thought Expires was active.

Does this rule apply to all types of resources?

Yes. HTML, CSS, JS, images, PDF: it makes no difference. As soon as a Cache-Control header with max-age is present, Expires becomes irrelevant. Modern CDNs (Cloudflare, Fastly, Akamai) apply the same logic: they read max-age as a priority.

Beware of inherited configurations that mix both without a valid reason. Some older CMS generate Expires by default, and a plugin or vhost adds max-age on top. The risk? Believing you control caching via Expires when it's actually max-age that drives it, with values you aren't aware of.

  • Max-age always overrides Expires when both are present in the HTTP response.
  • The duration is counted in seconds from the response, not in fixed dates like Expires.
  • Crawlers, browsers, and CDNs apply this priority without exception.
  • Server configurations (Apache, Nginx, IIS) can inject max-age without you seeing it in your application code.
  • Checking the actual headers with curl or DevTools is essential to avoid surprises.

SEO Expert opinion

Is this statement consistent with real-world observations?

Completely. Tests across thousands of sites show that max-age consistently takes precedence. CDNs and browsers do not make exceptions: if max-age is present, Expires is irrelevant. Google is simply reiterating a long-established HTTP/1.1 standard, but many practitioners still overlook it.

The real concern is the confusion inherited from the past. Expires dates back to HTTP/1.0, a time when Cache-Control did not exist. Today, keeping both in a configuration is often an oversight, rarely an informed choice. Result: configuration files full of contradictory directives that no one ever cleans up.

What nuances should we consider regarding this rule?

The priority of max-age does not mean Expires is useless. If you don't specify any Cache-Control, Expires takes over. In practice, this is rare: most modern servers inject at least one minimal Cache-Control, even if empty.

Another nuance: max-age can coexist with other Cache-Control directives like no-cache, must-revalidate, public, private. These directives modify caching behavior without voiding max-age. For example, "Cache-Control: max-age=3600, must-revalidate" entails revalidation after 3600 seconds, but max-age remains active. [To be verified]: some CDNs interpret must-revalidate differently based on their proprietary configuration, which can create disparities between browser cache and edge cache.

In what cases does this rule not apply?

If no Cache-Control header is sent, Expires regains total control. This is the only scenario where Expires truly plays its role. But as soon as any Cache-Control appears, even if empty or invalid, Expires loses all influence.

Be wary of application overrides. A JavaScript service worker can sidestep HTTP headers and manage caching in its own way. In this case, neither max-age nor Expires matter: it's the JS code that decides. Googlebot does not always treat service workers like a standard browser, which can create discrepancies between what you see locally and what the bot crawls.

Practical impact and recommendations

What concrete actions should be taken to master this priority?

Start by auditing your HTTP headers across all resource types. Use curl, wget, or Chrome DevTools to check actual responses. Many sites unknowingly send Expires and max-age because a plugin, CDN, or an Apache rule injects them automatically.

Next, choose a side. If you want precise control in seconds, focus entirely on max-age and remove Expires from your configurations. If you have legacy constraints with HTTP/1.0 clients (rare today), keep Expires as a fallback but be aware that max-age will override everything for modern clients.

What mistakes should be avoided in cache configuration?

Do not mix contradictory values between max-age and Expires without a valid reason. For example, an Expires in 30 days and a max-age=60 (1 minute): it's max-age that wins, causing your resources to expire in 1 minute, not 30 days. This type of configuration occurs when an older CMS generates Expires and a CDN adds max-age by default.

Another trap: forgetting that max-age=0 disables caching, even if Expires states "in a year". Some frameworks inject max-age=0 on dynamic pages to ensure freshness, but if you thought you were controlling caching with Expires, you end up with zero effective caching.

How can I ensure my site correctly adheres to this hierarchy?

Perform a full crawl with Screaming Frog or Oncrawl, enabling HTTP header capture. Filter resources that send both max-age and Expires, and then compare values. If you find inconsistencies, it's a sign your configuration is not under control.

Also, test actual behaviors: reload a page while clearing the cache, then reload it immediately. Check the Network tab to see if resources are served from the cache (status 304 or "from cache"). If behavior differs from what max-age indicates, investigate further: maybe a service worker or a Vary header is interfering.

  • Audit HTTP headers for all resources (HTML, CSS, JS, images) using curl or DevTools.
  • Remove Expires if max-age is already defined to avoid confusion.
  • Ensure max-age accurately reflects the desired cache duration (in seconds).
  • Check server configurations (Apache .htaccess, Nginx vhost, CDN rules) for automatic injections.
  • Test the actual behavior of the browser and CDN cache after modifications.
  • Document the caching strategy in a runbook to avoid regressions during updates.
Mastering the max-age > Expires hierarchy requires total visibility into the actual served HTTP headers. Server, CDN, and application configurations often overlap, creating invisible inconsistencies without thorough audits. For complex sites with multiple layers of caching (reverse proxy, multi-zone CDN, service workers), this optimization can quickly become technical. If you lack the time or expertise to untangle these configurations, consulting a specialized SEO agency in web performance can speed up compliance and avoid costly pitfalls in crawl budget or user experience.

❓ Frequently Asked Questions

Si je n'envoie que Expires sans max-age, Googlebot le respecte-t-il ?
Oui, si aucun Cache-Control n'est présent, Expires reprend le contrôle et Googlebot respectera la date d'expiration indiquée. Mais dès qu'un Cache-Control apparaît, même vide, Expires perd toute influence.
Max-age=0 et no-cache, c'est la même chose ?
Presque, mais pas exactement. Max-age=0 dit "expire immédiatement", tandis que no-cache demande une revalidation avant chaque utilisation. Les deux forcent une fraîcheur maximale, mais no-cache peut autoriser un cache conditionnel (304 Not Modified).
Un CDN peut-il ignorer max-age et appliquer sa propre politique ?
En théorie non, mais certains CDN ont des overrides configurables qui peuvent prolonger ou réduire la durée de cache indépendamment de max-age. Vérifie toujours les règles CDN pour éviter les surprises.
Dois-je supprimer Expires de toutes mes configs si j'utilise max-age ?
Pas obligatoire, mais recommandé pour éviter toute confusion. Garder Expires comme fallback pour d'hypothétiques clients HTTP/1.0 n'a plus vraiment de sens aujourd'hui, sauf besoin legacy très spécifique.
Comment savoir si un service worker court-circuite mes headers de cache ?
Inspecte l'onglet Application > Service Workers dans Chrome DevTools. Si un SW est actif, consulte son code pour voir s'il intercepte les fetch et applique une stratégie de cache custom, indépendante des headers HTTP.
🏷 Related Topics
Domain Age & History Web Performance

🎥 From the same video 1

Other SEO insights extracted from this same Google Search Central video · duration 2 min · published on 23/06/2009

🎥 Watch the full video on YouTube →

Related statements

💬 Comments (0)

Be the first to comment.

2000 characters remaining
🔔

Get real-time analysis of the latest Google SEO declarations

Be the first to know every time a new official Google statement drops — with full expert analysis.

No spam. Unsubscribe in one click.