LiteSpeed Web Server has a built-in page cache that can serve static HTML without executing PHP. Here's how I configured it for TopVideoHub, a dynamic video platform that needs different cache strategies for different page types.
Why LiteSpeed Cache?
Compared to Varnish or Redis-based page caching:
- Zero additional infrastructure — No separate cache server to manage
- Header-based control — Set cache policies from PHP code
- Tag-based purging — Invalidate specific pages without clearing everything
- ESI support — Mix cached and dynamic content on the same page
Basic Setup
Enable caching in .htaccess:
<IfModule LiteSpeed>
CacheEnable public /
CacheLookup on
</IfModule>
Then control caching from PHP via response headers:
// Cache this page for 3 hours
header('X-LiteSpeed-Cache-Control: public, max-age=10800');
Page-Type Cache Strategies
Different pages need different TTLs. Here's what works for a video platform:
enum PageType {
case HOME;
case CATEGORY;
case WATCH;
case SEARCH;
case API;
}
class CachePolicy {
public static function apply(PageType $type, string ...$tags): void {
$config = match($type) {
PageType::HOME => ['ttl' => 10800, 'stale' => 7200],
PageType::CATEGORY => ['ttl' => 10800, 'stale' => 7200],
PageType::WATCH => ['ttl' => 21600, 'stale' => 86400],
PageType::SEARCH => ['ttl' => 600, 'stale' => 1800],
PageType::API => ['ttl' => 3600, 'stale' => 3600],
};
header(sprintf(
'X-LiteSpeed-Cache-Control: public, max-age=%d, stale-while-revalidate=%d',
$config['ttl'],
$config['stale']
));
if (!empty($tags)) {
header('X-LiteSpeed-Tag: ' . implode(', ', $tags));
}
}
}
Usage in controllers:
// Home page: 3h cache, 2h stale-while-revalidate
CachePolicy::apply(PageType::HOME, 'page:home');
// Category page: 3h cache, tagged by category
CachePolicy::apply(PageType::CATEGORY, 'page:category', 'cat:music');
// Watch page: 6h cache (content rarely changes)
CachePolicy::apply(PageType::WATCH, 'page:watch', 'video:abc123');
// Search: 10min cache (results should be relatively fresh)
CachePolicy::apply(PageType::SEARCH, 'page:search');
Tag-Based Purging
When new trending data arrives, purge only affected pages:
class CachePurger {
public static function purgeTag(string ...$tags): void {
foreach ($tags as $tag) {
header('X-LiteSpeed-Purge: tag=' . $tag, false);
}
}
public static function purgeAll(): void {
header('X-LiteSpeed-Purge: *');
}
public static function afterFetch(array $updatedCategories): void {
// Always purge home page after new data
self::purgeTag('page:home');
// Purge affected category pages
foreach ($updatedCategories as $cat) {
self::purgeTag('cat:' . $cat);
}
}
}
This is dramatically better than purging everything. When music videos are fetched, only the home page and music category pages are purged. Gaming pages remain cached.
Vary By Region
On TopVideoHub, the same URL can show different content based on the region query parameter. LiteSpeed needs to cache these as separate entries:
// Tell LiteSpeed to vary cache by region parameter
header('X-LiteSpeed-Vary: value=region');
Now /?region=JP and /?region=KR are cached as separate entries, each with their own TTL and tags.
Monitoring Cache Performance
Check if a response came from cache by inspecting headers:
curl -sI https://topvideohub.com/ | grep -i litespeed
# X-LiteSpeed-Cache: hit
hit means LiteSpeed served from cache (no PHP executed). miss means PHP ran. After warmup, you should see 95%+ hit rate on a content platform.
Results at TopVideoHub
- Cache hit rate: 96%
- Cached response time: ~12ms
- Uncached response time: ~120ms
- Monthly hosting cost: Under $10
LiteSpeed's built-in cache turns a $10/month shared hosting plan into a high-performance content delivery platform. For PHP applications, it's the most underappreciated performance tool available.
Top comments (0)