Caching dynamic web pages (page may be 99% static but contain some dynamic content)
Categories:
Optimizing Performance: Caching Dynamic Web Pages with Mixed Content

Learn effective strategies for caching web pages that are mostly static but contain dynamic elements, improving performance and user experience across various programming languages.
In modern web development, many pages are not entirely static or entirely dynamic. Often, a significant portion of a page's content remains constant, while small, personalized sections (like a user's name, shopping cart count, or real-time updates) change frequently. Caching such '99% static, 1% dynamic' pages presents a unique challenge: how do you leverage the benefits of caching for the static parts without serving stale dynamic content? This article explores various techniques and best practices for intelligently caching these mixed-content pages, ensuring both performance and data freshness.
Understanding the Caching Challenge
Traditional full-page caching works well for purely static pages, storing the entire HTML output and serving it directly. For purely dynamic pages, caching might be applied at the data layer or through fragment caching. However, when a page combines both, a naive full-page cache would either serve outdated dynamic content or be bypassed entirely, negating its benefits. The goal is to cache the static shell of the page and dynamically inject or update the personalized components.
flowchart TD A[User Request] --> B{Page in Cache?} B -- No --> C[Generate Full Page] C --> D[Store Static Shell in Cache] D --> E[Serve Page to User] B -- Yes --> F{Dynamic Content Present?} F -- No --> E F -- Yes --> G[Serve Cached Static Shell] G --> H[Fetch Dynamic Content (AJAX/ESI)] H --> I[Inject/Update Dynamic Content] I --> E
Caching workflow for pages with mixed static and dynamic content.
Strategies for Mixed-Content Caching
Several strategies can be employed to effectively cache pages with dynamic elements. The choice depends on the complexity of the dynamic parts, the server-side technology, and the desired level of caching granularity.
1. Client-Side Rendering with AJAX
This is one of the most common and flexible approaches. The server renders a mostly static HTML page, which is then cached. Dynamic content areas are left empty or contain loading indicators. Once the page loads in the browser, JavaScript makes AJAX requests to fetch the dynamic data, which is then injected into the appropriate DOM elements. This offloads the dynamic content generation to separate, smaller API endpoints that can be cached independently or not at all.
<!DOCTYPE html>
<html>
<head>
<title>My Dynamic Page</title>
</head>
<body>
<h1>Welcome to our site!</h1>
<div id="user-greeting">Loading user data...</div>
<p>This is static content that can be cached.</p>
<div id="realtime-updates"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Fetch user greeting
fetch('/api/user/greeting')
.then(response => response.json())
.then(data => {
document.getElementById('user-greeting').innerText = 'Hello, ' + data.name + '!';
})
.catch(error => console.error('Error fetching user greeting:', error));
// Fetch real-time updates
setInterval(() => {
fetch('/api/updates')
.then(response => response.json())
.then(data => {
document.getElementById('realtime-updates').innerText = 'Latest update: ' + data.message;
})
.catch(error => console.error('Error fetching updates:', error));
}, 5000); // Update every 5 seconds
});
</script>
</body>
</html>
HTML structure with placeholders for dynamic content and client-side AJAX calls.
2. Server-Side Includes (SSI) or Edge Side Includes (ESI)
SSI and ESI allow you to define 'holes' in your static HTML content that are filled with dynamic content at the server or edge caching layer. SSI is typically handled by the web server (e.g., Nginx, Apache), while ESI is a more powerful standard often used by CDNs or specialized ESI processors. The main page can be cached, and the included dynamic fragments are fetched separately and inserted before serving the page to the user. This keeps the dynamic logic off the main application server for cached requests.
<!DOCTYPE html>
<html>
<head>
<title>My Dynamic Page</title>
</head>
<body>
<h1>Welcome to our site!</h1>
<!--#include virtual="/dynamic-greeting.html" -->
<p>This is static content that can be cached.</p>
<!--#include virtual="/dynamic-updates.html" -->
</body>
</html>
Example of an HTML page using Server-Side Includes (SSI) directives.
3. Fragment Caching (Server-Side)
Many web frameworks (like Ruby on Rails, Django, Laravel, and some PHP frameworks) offer built-in fragment caching. This allows you to cache specific blocks or 'fragments' of a page's HTML output. The main page template is processed, but when it encounters a cached fragment, it retrieves it directly from the cache instead of re-rendering. This is effective when dynamic parts are well-defined and encapsulated within the server-side rendering process.
Ruby on Rails
<% cache 'user_greeting_partial' do %>
Static content here.
<% cache ['realtime_updates', Time.now.to_i / 60] do %>
PHP (Laravel Blade)
@cache('user_greeting_partial')
Static content here.
@cache('realtime_updates', 60) {{-- Cache for 60 seconds --}}
Python (Django)
{% load cache %}
{% cache 3600 user_greeting_partial %}
Static content here.
{% cache 60 realtime_updates %}
4. Cache Invalidation and Keying
Regardless of the strategy, effective cache invalidation is critical. For dynamic content, ensure that the cache is cleared or updated when the underlying data changes. For fragment caching, consider using cache keys that incorporate relevant data identifiers or timestamps to ensure freshness. For example, a user's profile fragment might be keyed by user_id
and a version number derived from their last update time.
1. Identify Dynamic Zones
Carefully analyze your web page to pinpoint exactly which sections contain dynamic, user-specific, or frequently changing content. Mark these areas for special handling.
2. Choose a Strategy
Based on your application's architecture, framework, and the nature of the dynamic content, select the most appropriate caching strategy (AJAX, ESI/SSI, or fragment caching).
3. Implement Caching Logic
Apply the chosen caching mechanism. This might involve adding JavaScript for AJAX, configuring your web server for ESI/SSI, or using your framework's built-in fragment caching features.
4. Set Up Invalidation
Establish clear rules and mechanisms for invalidating or updating cached content when the underlying dynamic data changes. This is crucial to prevent serving stale information.
5. Monitor and Optimize
After deployment, monitor your page load times and cache hit rates. Adjust cache durations and strategies as needed to find the optimal balance between performance and data freshness.