The State of Caching and Interoperability Between Application Types

Table of Contents

1. Summary

The state of caching and interoperability between application types as of 2016. Three patterns dominate asset naming on CDNs: version-pinned paths, content-hash suffixes, and rolling build identifiers.

1.1. Naming conventions

Observable from HTTP Archive trends data:

1.1.1. CDN: version-pinned path

<link rel="stylesheet"
      href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">

The version (3.3.5) is part of the path. Cache-friendly: the URL never changes for a given version. Drawback: no automatic invalidation when a patch is released.

1.1.2. Yahoo: content-hash suffix

css/bundle.c60a6d54.css

The hash (c60a6d54) is derived from the file contents. Any change to the CSS produces a new hash, a new URL, and a guaranteed cache miss. This is the pattern that won — fingerprinting is now the default in every major framework.

1.1.3. Microsoft: rolling build identifier

https://assets.onestore.ms/cdnfiles/onestorerolling-1607-15000/shell/v3/scss/shell.min.css

The 1607-15000 encodes the build (Windows 10 Anniversary Update era). Hybrid: the build number changes with each release cycle, but the path is not content-addressed.

2. Tools (2016 baseline)

2.1. Rails Asset Pipeline (Sprockets)

Rails Asset Pipeline Guide — Sprockets compiles, concatenates, and fingerprints assets. The application-<hash>.css naming convention introduced fingerprinting to the Rails ecosystem.

2.2. Webpack

Webpack Caching Guide — content-hash output filenames ([name].[contenthash].js). The 2015-era integration with Rails via webpacker gem is now deprecated.

3. 2026 postscript

The landscape changed substantially since 2016.

3.1. Rails asset pipeline lineage

Years Era Tool Pattern
2011–2022 Rails 3.1–6.1 Sprockets application-<digest>.css
2017–2022 Rails 5.1–7.0 Webpacker (deprecated) Webpack bundles
2021-- Rails 7+ importmap-rails No bundler; native ES modules
2022-- Rails 7+ Propshaft Simpler fingerprinting, no compilation
2024-- Rails 8 Solid Cache Database-backed fragment caching

Turbo Drive (Hotwire) replaces Turbolinks and changes full-page cache invalidation: the browser fetches HTML and morphs the DOM rather than doing full page loads. This makes ETag / 304 Not Modified more important than asset fingerprinting for navigation.

3.2. HTTP caching headers

Header Significance
Cache-Control: immutable RFC 8246 (2017). Fingerprinted assets should never be revalidated.
stale-while-revalidate RFC 5861. Serve stale while fetching fresh in background.
Vary: Accept-Encoding Required for CDN cache key normalization with gzip/brotli.
cdn-cache-control CDN-specific TTL, separate from browser Cache-Control.

3.3. Browser cache partitioning

Safari partitioned third-party caches first (~2013; ITP 2.1 in Safari 12.1, early 2019). Chrome 86 (October 2020, gradual rollout) and Firefox 85 (January 2021) followed. The HTTP cache is now keyed by a tuple of (top-level site, frame site), not just the resource URL. A library loaded from cdnjs.cloudflare.com on site A occupies a different cache slot than the same URL on site B.

This eliminated the cross-site cache reuse that made shared CDNs a performance win. The extra DNS lookup and TLS handshake to a third-party origin now has no cache benefit to offset it. For assets like fonts, self-hosting removes that overhead entirely (Wicki 2020). For large libraries, the tradeoff depends on CDN geography and server configuration — but the "everyone shares one cached copy" argument is gone.

3.4. Edge computing

CDN edge functions move computation to the cache layer. The cache is no longer passive storage — it can transform responses, personalize content, and enforce access control without a round-trip to the origin.

3.4.1. Major platforms

Six platforms define the current landscape. They split on runtime: V8 isolates (near-zero cold start, JavaScript/Wasm only) versus containers or Lambda processes (flexible runtime, second-scale cold start).

Platform Announced / GA Runtime CPU limit Memory
Cloudflare Workers Sep 2017 / Mar 2018 V8 isolates 10 ms free; 30 s paid 128 MB
Vercel Edge Functions Dec 2022 GA V8 isolates (no MicroVM) 50 ms CPU / invocation not published
Netlify Edge Functions 2022 Deno (V8 + Rust) 50 ms CPU 512 MB per deploy
AWS CloudFront Functions 2021 JS ECMAScript 5.1 submillisecond 2 MB
AWS Lambda@Edge 2017 Node.js / Python 30 s viewer; 30 s origin 128 MB–10 GB
Fastly Compute (was Compute@Edge) 2019 beta WebAssembly (WASI) billed per vCPU-ms 128 MB min
Deno Deploy 2021 V8 isolates (Deno runtime) not published 512 MB

Cold-start comparison: CloudFront Functions and Cloudflare Workers achieve submillisecond startup because isolates share a warm V8 heap. Fastly Compute reaches ~50 µs via Wasm module instantiation. Lambda@Edge requires container provisioning and can take seconds on a cold path.

Cloudflare Workers pricing: free tier 100,000 req/day, 10 ms CPU. Paid ($5/month minimum): 10 M req/month included then $0.30/million; 30 M CPU-ms included then $0.02/million. Vercel Edge: 500,000 execution units/month free (Hobby); 1 M (Pro); 1 unit = 50 ms CPU. CloudFront Functions: $0.10/million invocations.

3.4.2. Computation at the edge changes the caching model

Traditional CDN caching is passive: store the response, serve the stored copy, expire on TTL. Edge functions make caching active.

A/B testing. The worker reads a cookie to determine the user's cohort, rewrites the request path to /test or /control, and fetches from the corresponding cached variant. No origin round-trip for the routing decision. New visitors are randomly assigned and the cohort cookie is set on the response.

Geolocation-based content. Cloudflare injects CF-IPCountry and Vercel injects X-Vercel-IP-Country into every request. A worker can serve a different cached variant per country using Vary: X-Vercel-IP-Country. Vercel's CDN respects this and stores separate cache entries per country value.

Authentication at the edge. JWT validation (HMAC or RS256 via SubtleCrypto) runs in the worker before the request reaches the origin. CloudFront Functions can inspect Authorization headers and return a 403 in submillisecond time; the origin is never hit for unauthorized requests.

Response transformation. Workers rewrite HTML (inject banners, swap canonical URLs, modify <head>), inject security headers (Content-Security-Policy, Strict-Transport-Security), or transform JSON before it is placed in the CDN cache. The cache stores the transformed response, not the raw origin response.

Stale-while-revalidate in the worker. On a stale hit, the worker calls waitUntil() to revalidate the origin in the background after returning the stale copy. Vercel natively honours s-maxage=N, stale-while-revalidate=Z in Cache-Control headers from functions. The Vercel-CDN-Cache-Control header allows different TTLs for the Vercel edge, downstream CDNs, and the browser independently.

3.4.3. Key technical constraints

These constraints are common across V8-isolate platforms and shape what edge functions can and cannot do.

Constraint Detail
No filesystem No server filesystem access. Assets must be bundled or fetched from KV/R2/D1.
No native modules Wasm available; Node.js .node addons are not.
CPU time cap 10–50 ms covers routing, header manipulation, and SubtleCrypto operations. Not sufficient for image transcoding or ML inference without Wasm.
Memory cap 128–512 MB. In-memory state is per-isolate and does not persist across requests.
Startup budget On Cloudflare Workers, global scope must complete within 1 second.
Date.now() does not advance On Cloudflare, Date.now() returns the time of the last I/O event. Timing-sensitive logic must account for this.
No eval() Dynamic code evaluation blocked on all V8-isolate runtimes.
Request body access CloudFront Functions cannot read the request body. Lambda@Edge can.

These constraints explain the CloudFront split: CloudFront Functions for header and URL manipulation, Lambda@Edge for network calls, file access, or heavier compute.

3.4.4. The edge database trend

Edge functions became substantially more capable when database primitives moved to the same network tier.

Product Type Launched Notes
Cloudflare KV Key-value 2018 beta Eventually consistent; global replication. Free: 100K reads/day, 1K writes/day, 1 GB. Paid: 10 M reads/month, $0.50/million additional.
Cloudflare D1 SQLite SQL Sep 2023 open beta Strong consistency per database. Free: 25 B rows read/month, 50 M rows written, 5 GB storage.
Cloudflare R2 Object storage 2022 S3-compatible; no egress fees. $0.015/GB-month.
Vercel KV Redis-compatible 2023; discontinued Dec 2024 Migrated to Upstash. New projects use Marketplace Redis integrations.
Turso (libSQL) SQLite fork 2023 Rust rewrite of SQLite. Concurrent writes, vector search, per-tenant databases. Wasm-portable.
Neon Serverless Postgres 2022 Scale-to-zero with pgBouncer. Database branching, point-in-time restore.

The KV / SQL split reflects two use cases. KV (Cloudflare KV, Upstash Redis) stores session tokens, feature flags, and rate-limit counters — small values, high read volume, eventual consistency acceptable. SQL at the edge (D1, Turso, Neon) stores relational data close to compute.

Cloudflare KV is eventually consistent: writes propagate to all edge nodes within ~60 seconds. D1 offers strong consistency within a single database file; global read replicas trade recency for read throughput.

3.4.5. Solid Cache and the Rails 8 connection

Rails 8 (2024) ships Solid Cache as the default fragment cache store, replacing the Redis/Memcached pattern dominant since Rails 4. Solid Cache stores fragments in a SQLite3 database on SSD (storage/production_cache.sqlite3 by default), using FIFO eviction. Configuration: max_age, max_size (e.g., 256 MB), encrypt, databases (sharding across multiple SQLite files).

  Solid Cache (Rails 8) Cloudflare KV Cloudflare D1
Storage medium SQLite on SSD Distributed key-value store SQLite (edge-replicated)
Consistency Strong (single DB) Eventual (~60 s propagation) Strong per DB; eventual for replicas
Location Origin server 300+ edge PoPs globally 300+ edge PoPs globally
Query model SQL (ActiveRecord) key.get / key.put Full SQL
Eviction FIFO, configurable max_size TTL-based Manual or TTL
Encryption Optional (encrypt: true) Not native Not native
Rails integration Native (ActiveSupport::Cache) Via HTTP API or gem Via HTTP API or gem

Solid Cache is appropriate when the cache lives on the same machine as the Rails process — a local SQLite read is sub-millisecond. Cloudflare KV or D1 is appropriate when the cache must be served from an edge location decoupled from any origin process. The patterns are complementary: a Rails app can use Solid Cache for server-side fragment caching and Cloudflare KV for session tokens served at the edge.