Skip to main content

A Guide to HTTP Cache Control Headers

Cache Control Headers are a powerful tool for controlling how browsers and caches store and serve your website's content. By setting the right headers, you can improve your website's performance, and in some cases, have your users experience near-instant page loads.

HTTP Cache-Control Headers

Caching improves website performance. A well-implemented caching strategy reduces bandwidth usage, server load, and page load times. At the heart of HTTP caching is the Cache-Control header, which gives you fine-grained control over how browsers and intermediary proxies cache your content.

This guide explains the most common Cache-Control headers, their practical applications, and provides real-world examples for different scenarios.

note

This page provides an overview of HTTP cache control headers.

Do you want a more comprehensive guide on browser caching? Check out our How To Leverage Browser Caching to Improve Site Speed post.

Introduction to HTTP Caching

HTTP caching works by storing copies of resources (like HTML pages, images, or scripts) closer to the user — either in their browser or in intermediate caches like proxies and CDNs. When a user requests a resource that is cached, the cached copy can be served without hitting the origin server, resulting in faster response times and reduced server load.

The way a resource is cached is controlled primarily through HTTP headers, with the Cache-Control header being the most important one.

tip

You can specify one or more directives within the Cache-Control header.

For example, Cache-Control: max-age=3600, public sets a maximum cache duration of 1 hour and allows the resource to be cached by any cache. However, some examples below show only one directive for simplicity. In practice, you can combine multiple directives to create more customized caching strategies.

How to view cache-control headers for resources on your website

If you want to check how your website uses the cache-control header to specify browser caching behavior, you can run a free website speed test with DebugBear.

The request waterfall view lists all the resources loaded by your website. You can click on each one and open the Headers tab to see what response headers, including cache-control were sent by the server.

Cache control header in a DebugBear test result

Use no-store when you don't want any caching

What it does: The no-store directive instructs browsers and caches to not store any version of the resource. It is suitable for sensitive data, or regularly changing data that you don't want to be cached.

Cache-Control: no-store

Example URLs:

  • /dashboard: Has sensitive user data that updates frequently.
  • /account/bank-balance: Shows the user's bank balance, this should never be cached.
  • /api/stock-prices/GOOG: The current stock price of Google, which can change frequently.

When to use: Apply this header to pages containing personal or sensitive information, such as account pages, checkout flows, or anything containing PII (Personally Identifiable Information). Also useful for real-time data that needs to always be fresh, like stock prices or live sports scores.

Use no-cache for content that requires validation

What it does: The no-cache directive doesn't mean "don't cache" - it means:

You can cache, but you must revalidate with the server before using the cached copy.

Cached resources must be validated with the origin server before each reuse, even if they haven't expired.

Cache-Control: no-cache

Example URLs:

  • /news: Content that changes frequently but doesn't need real-time updates.
  • /products/popular: A list of popular products that updates regularly.
  • /weather/current: Current weather information that updates periodically.

When to use: Apply this header to content that changes frequently. It strikes a balance between ensuring relatively fresh content while potentially saving bandwidth when the resource hasn't changed.

Use private for user-specific content

What it does: The private directive specifies that the response is intended for a single user and must not be stored in shared caches like CDNs or proxies. It can still be stored in a browser's cache.

Cache-Control: private
tip

When a cache duration (e.g. max-age) or revalidation directive (e.g. must-revalidate) is not specified, a browser or CDN will make their own decisions about caching based on the content type and other factors.

Example URLs:

  • /my-account/profile: User profile information.
  • /orders/history: User's order history.
  • /recommendations: Personalized content recommendations.

When to use: Apply this header to any user-specific content that shouldn't be shared with other users but can be cached in the user's browser. This is particularly important for content delivered after authentication.

Use public for shareable content

What it does: The public directive explicitly marks a response as cacheable by any cache. It's useful for content that is the same for all users and can be shared among them.

Cache-Control: public

Example URLs:

  • /about-us: Public information about your company.
  • /help/faq: Frequently asked questions page.
  • /blog/post-1: Blog posts that are the same for all users.

When to use: Use this for content that is not personalized and can be shared among all users. This is especially useful in combination with other directives like max-age.

Use max-age to control how long content is fresh

What it does: The max-age directive specifies the maximum amount of time in seconds that a resource is considered fresh. After this time, the browser will request a new copy from the server.

# Cache content for 1 hour (3600 seconds)
Cache-Control: max-age=3600
tip

You may be wondering, if you don't specify private or public, what's the default? In most cases, the default is public, meaning the response can be cached by any cache such as a CDN or proxy. However, both public and private caches may use their own heuristics to determine caching behavior.

Example URLs:

  • /blog/post-1: Blog content that is relatively static, but updated occasionally.
  • /images/logo.png: Company logo that rarely changes.
  • /css/styles.css: CSS files that change infrequently.

When to use: Use this for resources that don't change often. The duration should be based on how frequently the content changes. For example, use a shorter duration for a news homepage (possibly 5-15 minutes) and a longer duration for static assets like images or JavaScript libraries - possibly days, weeks, or even a year.

Use s-maxage for CDN-specific cache control

What it does: The s-maxage directive works like max-age but applies only to shared caches (like CDNs and proxies).

The following example sets a 10-minute cache duration for user browsers and a 1-hour cache duration for CDNs:

Cache-Control: max-age=600, s-maxage=3600

Example URLs:

  • /api/public-data: Public API responses that can be cached differently for browsers and CDNs.
  • /images/hero-banner.jpg: Large images that benefit from longer caching at the CDN level.
  • /product-catalog: A product catalog that updates daily on the CDN but might need more frequent updates in the browser.

When to use: Use when you want different caching behaviors between user browsers and CDNs. This is often useful for reducing load on your servers by allowing CDNs to cache content longer, while making sure browsers get somewhat fresher content.

Use max-age=0 to force revalidation

What it does: Setting max-age=0 forces the cache to revalidate the resource with the origin server on every request.

Cache-Control: max-age=0

Example URLs:

  • /status/system: System status information that needs to be current.
  • /api/stock-prices: Stock prices that update frequently.
  • /news/latest: Breaking news that should always be fresh.

When to use: This is similar to no-cache - one difference being that no-cache will always trigger revalidation with the server, while max-age=0 can allow a cache to use a stale response if the server is unreachable, however this behavior is not guaranteed.

Use must-revalidate to prevent using stale content

What it does: The must-revalidate directive tells caches they must revalidate the resource with the origin server before using a cached copy, meaning that stale content is never served.

This example sets a 1-hour cache duration, meaning if the resource is fresh (within 1 hour), it can be used without revalidation. However, if it's stale (older than 1 hour), it must be revalidated with the server before use:

Cache-Control: max-age=3600, must-revalidate

Example URLs:

  • /api/user-profile: User profile information that must be current.
  • /feed: Social media feed that should always be fresh.
  • /news/latest: Breaking news that should never be stale.

When to use: Apply this header to content where serving stale data could lead to problems, such as outdated pricing or incorrect user information.

tip

Revalidation doesn't always mean transferring the entire content again. The server can respond with a 304 Not Modified status code, indicating that the content hasn't changed, and the cache can continue using the cached copy.

In such a case, the server only sends the headers, not the content. In addition, the browser will update the resource so the new Cache-Control headers are applied.



Use immutable for versioned assets

What it does: The immutable directive tells the browser that the content of the file will never change, so the browser doesn't need to revalidate it even when the user refreshes the page.

Cache-Control: public, max-age=31536000, immutable

Example URLs:

  • /assets/main.f4fa2b.js: JavaScript file with a content hash in the filename.
  • /css/styles.ae3f66.css: CSS file with a content hash in the filename.
  • /images/logo.v2.png: Versioned image file.

When to use: Use for static resources that include a fingerprint (hash) or version number in their URL, ensuring that any change to the content results in a new URL. This is perfect for resources that follow the cache-busting pattern, where each change creates a new file with a different name.

Use stale-while-revalidate for background updates

What it does: The stale-while-revalidate directive allows the cache to serve a stale response while it revalidates in the background, making the next response fresh without the user waiting.

Consider this example:

Cache-Control: max-age=604800, stale-while-revalidate=86400

These directives define seven days as the maximum cache duration, with a one-day window for serving stale content while revalidating. This means:

  • That content up to seven days old is considered fresh and can be served immediately without a network request.
  • That content between seven and eight days old is considered stale and must be revalidated in the background.
    • The fresh content that is fetched in the background will be used for future requests.

Example URLs:

  • /api/user-count: API response showing user counts that can be stale for a short period.
  • /widgets/sidebar: UI components that can be momentarily stale.
  • /data/stats: Statistics that don't need to be perfectly real-time.

When to use: Apply this header to resources where some staleness is acceptable to improve performance. The stale content is shown to the user immediately while a fresh copy is fetched in the background, providing a good balance between performance and freshness.

Use private, no-cache for authenticated content

What it does: This combination ensures that user-specific content is not stored in shared caches and must be revalidated before use, even from browser cache.

Cache-Control: private, no-cache

Example URLs:

  • /account/settings: User account settings.
  • /messages/inbox: User's private messages.
  • /api/feed: Personalized content feed.

When to use: Apply this header to authenticated, user-specific content that changes frequently and shouldn't be shared with other users. This provides security while still allowing for efficient revalidation through the browser cache.

Use no-transform to prevent modifications

What it does: The no-transform directive prevents intermediaries (like proxies or CDNs) from modifying the content of the response, such as compressing images or transcoding media.

Cache-Control: no-transform

Example URLs:

  • /images/high-res/photo.jpg: High-quality images where quality shouldn't be modified.
  • /downloads/document.pdf: PDF files that shouldn't be modified.
  • /media/video.mp4: Video files that shouldn't be transcoded.

When to use: Apply this header to resources where any modification by intermediaries could negatively impact quality or functionality, such as high-resolution images, or media files with specific encoding.

Use stale-if-error for resilience

What it does: The stale-if-error directive allows a cache to return a stale response when an error occurs during revalidation. This means that content remains available even if the server is temporarily unreachable.

This example sets a one-hour cache duration, allowing the cache to serve stale content for up to 24 hours if the server is unreachable or returns an error during revalidation:

Cache-Control: max-age=3600, stale-if-error=86400

Example URLs:

  • /api/product-recommendations: Product recommendations that could be stale during outages.
  • /common/footer: Site components that should remain available during partial outages.
  • /images/logo.png: Company logo that should always be available.

When to use: Apply this header to non-critical resources where having stale content is better than no content during server outages or maintenance.

Test cache-control headers with synthetic testing

If you want to see how fast your website loads with the right caching settings in place, try the warm load feature in DebugBear's synthetic website monitoring product.

Warm load in DebugBear

You'll often find that pages load extremely fast if previously loaded resources can be re-used.

After the initial visit, websites are usually much faster thanks to caching.

DebugBear warm load test result

In the waterfall, resources that are loaded from the cache are marked by a "cache" badge for the request.

DebugBear cache badges

DebugBear can track your synthetic metrics over time, provide industry benchmarks based on Google CrUX data, and collect real-user metrics and debug data on how actual visitors experience your website.

Illustration of website monitoringIllustration of website monitoring

Monitor Page Speed & Core Web Vitals

DebugBear monitoring includes:

  • In-depth Page Speed Reports
  • Automated Recommendations
  • Real User Analytics Data

Conclusion

By using the appropriate Cache-Control headers, you can improve your website's performance and user experience.

It's important to note that for caching to be effective, you also need to configure validators like ETags or Last-Modified headers, especially when using directives like no-cache that rely on validation. Additionally, a cache-busting strategy for static assets is key when implementing long-term caching.