npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@usex/cache-control

v1.0.0

Published

Lightning-fast Cache-Control header parser & builder. Craft perfect caching strategies with a fluent API, smart defaults, and zero dependencies. Because your CDN deserves better than string concatenation.

Readme

🎯 What is Cache Control?

Cache Control is your type-safe companion for managing HTTP cache headers. No more string concatenation, no more guessing directive names, no more cache invalidation nightmares. Just a clean, fluent API that makes cache management actually enjoyable.

🚀 Quick Start

npm install @usex/cache-control
import { CacheControl } from '@usex/cache-control';

// Build cache headers with confidence
const cache = new CacheControl()
  .set('public', true)
  .set('max-age', 3600)
  .set('s-maxage', 7200)
  .set('stale-while-revalidate', 60);

console.log(cache.toString());
// Output: "public, max-age=3600, s-maxage=7200, stale-while-revalidate=60"

🌟 Why Cache Control?

Before (The Dark Ages 🌑)

// String manipulation chaos
res.setHeader('Cache-Control', 'public, max-age=3600, s-maxage=' + (60 * 60 * 2));
res.setHeader('CDN-Cache-Control', 'max-age=7200'); // Hope you spelled it right!
res.setHeader('Vercel-CDN-Cache-Control', '...'); // What directives are even valid here?

After (The Enlightenment ✨)

const cache = new CacheControl()
  .set('public', true)
  .set('max-age', 3600)
  .set('s-maxage', 7200);

// Type-safe, readable, maintainable
res.setHeader('Cache-Control', cache.toString());

💪 Core Features

🎯 Type-Safe API

Full TypeScript support means your IDE catches mistakes before they hit production.

const cache = new CacheControl()
  .set('max-age', 3600)        // ✅ TypeScript knows this is valid
  .set('maxAge', 3600)         // ❌ TypeScript error: Invalid directive
  .set('max-age', '3600');     // ❌ TypeScript error: Must be number

🔧 Fluent Chainable Interface

Build complex cache strategies with elegant method chaining.

const cache = new CacheControl()
  .set('private', true)
  .set('max-age', 300)
  .set('must-revalidate', true);

📝 Parse Existing Headers

Working with incoming requests? Parse headers like a pro.

const incomingHeader = req.headers['cache-control'];
const cache = new CacheControl(incomingHeader);

// Modify and send back
cache.set('max-age', cache.get('max-age') * 2);
res.setHeader('Cache-Control', cache.toString());

🌐 Multi-Header Support

Support for platform-specific CDN headers out of the box.

import { CacheControl, getCDNHeader, applyCDNHeaders } from '@usex/cache-control';

// Create your cache strategy
const cache = new CacheControl()
  .set('public', true)
  .set('s-maxage', 7200)
  .set('stale-while-revalidate', 60);

// Get CDN-specific headers
const vercelHeader = getCDNHeader(cache, 'vercel');
// { header: 'Vercel-CDN-Cache-Control', value: 'public, s-maxage=7200, stale-while-revalidate=60' }

// Apply to your response
applyCDNHeaders(res, cache, ['vercel', 'cloudflare']);
// Sets both Vercel-CDN-Cache-Control and Cloudflare-CDN-Cache-Control headers

📚 Complete API Reference

Constructor

// Start fresh
const cache = new CacheControl();

// Parse existing header
const cache = new CacheControl("public, max-age=3600");

Available Directives

📊 Cache-Control Directives Reference

| Directive | Type | Description | Example | |-----------|------|-------------|---------| | max-age | Number | Browser Cache Duration - Specifies the maximum amount of time (in seconds) a resource is considered fresh. After this time expires, the cache must check with the origin server before using the cached copy | max-age=3600 | | s-maxage | Number | CDN Cache Duration - Overrides max-age for shared caches like CDNs and proxies. Allows different cache durations for browsers vs edge servers | s-maxage=7200 | | max-stale | Number | Accept Stale Content - Indicates the client is willing to accept a response that has exceeded its expiration time by up to the specified number of seconds. Useful for offline functionality | max-stale=300 | | min-fresh | Number | Require Fresh Content - Client wants a response that will still be fresh for at least the specified number of seconds. Ensures content validity for time-sensitive operations | min-fresh=60 | | no-cache | Boolean | Always Validate - Forces caches to submit the request to the origin server for validation before releasing a cached copy. Ensures users get the latest content while still benefiting from caching | no-cache | | no-store | Boolean | Never Store - The cache must not store either the request or response. Used for sensitive information like personal banking data or medical records | no-store | | no-transform | Boolean | Preserve Original - Intermediate caches or proxies must not modify the response body (no compression, image optimization, etc.). Critical for applications requiring exact byte-for-byte responses | no-transform | | only-if-cached | Boolean | Offline Mode - Client only wants a cached response and won't accept a network request. Returns 504 (Gateway Timeout) if no cached response is available. Perfect for offline-first applications | only-if-cached | | public | Boolean | Cacheable by All - Response may be cached by any cache, even if it would normally be non-cacheable. Explicitly marks responses as safe for CDN and browser caching | public | | private | Boolean | User-Specific - Response is intended for a single user and must not be stored by shared caches like CDNs. Browser cache only. Used for personalized content | private | | must-revalidate | Boolean | Strict Validation - Once stale, cache must not use the response without successful validation with the origin server. Prevents serving outdated content even in error scenarios | must-revalidate | | proxy-revalidate | Boolean | CDN Validation - Like must-revalidate but only applies to shared caches. Allows browsers to be more lenient while keeping CDN content strict | proxy-revalidate | | must-understand | Boolean | Cache Compatibility - Cache should only store the response if it understands the requirements for caching based on status code and request method. Ensures proper cache behavior | must-understand | | immutable | Boolean | Never Changes - Indicates the response body will not change over time. Browsers can skip revalidation even when user hits refresh. Perfect for versioned static assets | immutable | | stale-while-revalidate | Number | Background Refresh - Cache may serve stale content while asynchronously revalidating in the background. Improves perceived performance by avoiding loading delays | stale-while-revalidate=60 | | stale-if-error | Number | Fallback Content - Cache may serve stale content if the origin server responds with 5xx errors or is unreachable. Improves reliability during outages | stale-if-error=300 |

🕐 Time-Based Directives

cache
  .set('max-age', 3600)              // Browser cache: 1 hour
  .set('s-maxage', 7200)             // CDN cache: 2 hours
  .set('max-stale', 300)             // Accept content up to 5 min stale
  .set('min-fresh', 60)              // Require at least 1 min fresh
  .set('stale-while-revalidate', 60) // Serve stale while fetching fresh
  .set('stale-if-error', 300);       // Serve stale for 5 min on errors

🔒 Access Control Directives

cache
  .set('public', true)     // Any cache can store
  .set('private', true);   // Only browser can store

🚫 Validation Directives

cache
  .set('no-cache', true)         // Must revalidate before use
  .set('no-store', true)         // Don't store at all
  .set('must-revalidate', true)  // No stale content allowed
  .set('proxy-revalidate', true); // CDNs must revalidate

⚡ Performance Directives

cache
  .set('immutable', true)      // Content never changes
  .set('no-transform', true)   // Don't modify (compress, etc.)
  .set('must-understand', true); // Only cache if you understand

Methods

// Set a directive
cache.set('max-age', 3600);

// Get a directive value
const maxAge = cache.get('max-age'); // 3600

// Check if directive exists
if (cache.has('public')) {
  // ...
}

// Remove a directive
cache.delete('private');

// Clear all directives
cache.clear();

// Convert to string
const header = cache.toString();

// Create from existing header
const cache2 = CacheControl.from('public, max-age=3600');

// Get as JSON
const json = cache.toJSON();

🌍 CDN-Specific Headers

Cache Control provides built-in support for all major CDN providers. Each CDN uses its own header name for cache control directives.

Supported CDN Providers

| Provider | Header Name | Use Case | |----------|-------------|----------| | Vercel | Vercel-CDN-Cache-Control | Vercel Edge Network | | Cloudflare | Cloudflare-CDN-Cache-Control | Cloudflare CDN | | Fastly | Surrogate-Control | Fastly CDN | | AWS CloudFront | CloudFront-Cache-Control | Amazon CloudFront | | Akamai | Edge-Control | Akamai Edge | | Bunny CDN | Bunny-Cache-Control | Bunny CDN | | KeyCDN | X-KeyCDN-Cache-Control | KeyCDN | | Netlify | Netlify-CDN-Cache-Control | Netlify Edge | | Azure Front Door | X-Azure-Cache-Control | Microsoft Azure CDN | | Google Cloud CDN | X-Cloud-CDN-Cache-Control | Google Cloud CDN | | Alibaba CDN | Ali-Swift-Cache-Control | Alibaba Cloud CDN |

CDN Header Functions

import { 
  CacheControl, 
  getCDNHeader, 
  getCDNHeaders,
  applyCDNHeaders,
  getAllCDNProviders,
  isValidCDNProvider 
} from '@usex/cache-control';

// Create cache strategy
const cache = new CacheControl()
  .set('public', true)
  .set('s-maxage', 86400)
  .set('stale-while-revalidate', 60);

// Get single CDN header
const vercel = getCDNHeader(cache, 'vercel');
console.log(vercel);
// { 
//   header: 'Vercel-CDN-Cache-Control', 
//   value: 'public, s-maxage=86400, stale-while-revalidate=60' 
// }

// Get multiple CDN headers
const headers = getCDNHeaders(cache, ['vercel', 'cloudflare', 'fastly']);

// Apply directly to response
applyCDNHeaders(res, cache, 'vercel');
// or multiple providers
applyCDNHeaders(res, cache, ['vercel', 'cloudflare']);

// Get all available providers
const providers = getAllCDNProviders();

// Validate provider name
if (isValidCDNProvider('vercel')) {
  // TypeScript knows this is valid
}

Framework Integration

Express.js

import express from 'express';
import { CacheControl, applyCDNHeaders } from '@usex/cache-control';

const app = express();

app.get('/api/data', (req, res) => {
  const cache = new CacheControl()
    .set('public', true)
    .set('s-maxage', 3600)
    .set('stale-while-revalidate', 60);
  
  // Apply standard Cache-Control
  res.setHeader('Cache-Control', cache.toString());
  
  // Apply CDN-specific headers
  applyCDNHeaders(res, cache, 'vercel');
  
  res.json({ data: 'example' });
});

Fastify

import fastify from 'fastify';
import { CacheControl, applyCDNHeaders } from '@usex/cache-control';

const app = fastify();

app.get('/api/data', async (request, reply) => {
  const cache = new CacheControl()
    .set('public', true)
    .set('s-maxage', 3600)
    .set('stale-while-revalidate', 60);
  
  // Apply standard Cache-Control
  reply.header('Cache-Control', cache.toString());
  
  // Apply CDN-specific headers (works with Fastify's reply object)
  applyCDNHeaders(reply, cache, ['vercel', 'cloudflare']);
  
  return { data: 'example' };
});

Next.js API Routes

import { NextApiRequest, NextApiResponse } from 'next';
import { CacheControl, applyCDNHeaders } from '@usex/cache-control';

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const cache = new CacheControl()
    .set('public', true)
    .set('s-maxage', 86400)
    .set('stale-while-revalidate', 60);
  
  // Apply both standard and Vercel-specific headers
  res.setHeader('Cache-Control', cache.toString());
  applyCDNHeaders(res, cache, 'vercel');
  
  res.status(200).json({ data: 'example' });
}

CDN-Specific Examples

Vercel Edge Network

const vercelCache = new CacheControl()
  .set('public', true)
  .set('s-maxage', 31536000)  // 1 year at edge
  .set('stale-while-revalidate', 86400);  // 24 hours SWR

applyCDNHeaders(res, vercelCache, 'vercel');

Cloudflare CDN

const cloudflareCache = new CacheControl()
  .set('public', true)
  .set('s-maxage', 604800)  // 1 week
  .set('must-revalidate', true);

applyCDNHeaders(res, cloudflareCache, 'cloudflare');

Multi-CDN Setup

// When using multiple CDN providers
const multiCDNCache = new CacheControl()
  .set('public', true)
  .set('s-maxage', 3600)
  .set('stale-if-error', 86400);

// Apply to all your CDNs at once
applyCDNHeaders(res, multiCDNCache, ['vercel', 'cloudflare', 'fastly']);

Enterprise CDN Configuration

// AWS CloudFront + Azure Front Door
const enterpriseCache = new CacheControl()
  .set('public', true)
  .set('s-maxage', 7200)
  .set('max-age', 300)
  .set('stale-while-revalidate', 60);

applyCDNHeaders(res, enterpriseCache, ['aws-cloudfront', 'azure-front-door']);

🎮 Real-World Examples

Static Assets (1 Year Cache)

const staticAssets = new CacheControl()
  .set('public', true)
  .set('max-age', 31536000)
  .set('immutable', true);

API Responses (Cache with Revalidation)

const apiResponse = new CacheControl()
  .set('private', true)
  .set('max-age', 0)
  .set('must-revalidate', true);

Dynamic Content (Stale-While-Revalidate)

const dynamicContent = new CacheControl()
  .set('public', true)
  .set('max-age', 300)
  .set('stale-while-revalidate', 60)
  .set('stale-if-error', 3600);

User-Specific Content

const userContent = new CacheControl()
  .set('private', true)
  .set('max-age', 600)
  .set('must-revalidate', true);

Offline-First Application

// Request headers for offline support
const offlineRequest = new CacheControl()
  .set('only-if-cached', true)
  .set('max-stale', 86400); // Accept day-old content when offline

Time-Sensitive Content

// Ensure content is fresh for critical operations
const criticalData = new CacheControl()
  .set('no-cache', true)
  .set('min-fresh', 300); // Must be fresh for at least 5 minutes

🔥 Pro Tips

1. CDN vs Browser Caching

// Short browser cache, long CDN cache
const smartCache = new CacheControl()
  .set('public', true)
  .set('max-age', 300)      // 5 minutes for browsers
  .set('s-maxage', 86400);  // 24 hours for CDNs

2. Cache Invalidation Strategy

// Use stale-while-revalidate for better UX
const betterUX = new CacheControl()
  .set('public', true)
  .set('max-age', 300)
  .set('stale-while-revalidate', 60);

3. Security First

// Sensitive data should never be cached publicly
const secureData = new CacheControl()
  .set('private', true)
  .set('no-store', true);

🤝 Contributing

Got ideas? Found a bug? PRs are welcome! Check out our contributing guidelines.

📜 License

This project is licensed under the MIT License. See the LICENSE file for details.