Prompt and context
A cache gateway adds Warning: 110 - "Response is stale" and Warning: 111 - "Revalidation failed". Browsers and downstream services do not consistently surface these fields, and newer standards no longer recommend them. Explain the historical semantics, cache-validation boundary, replacement fields, and a lossless migration.
This question fits backend, gateway, CDN, and platform interviews. The key is separating protocol metadata, user errors, and internal observability rather than replacing one deprecated header with another free-form header.
What the interviewer is testing
A strong answer explains that Warning could describe message problems and that cache-related 1xx warnings could be removed after successful validation. It also notes that the field is deprecated because it was not broadly generated or surfaced, while parts of its information can be inferred from fields such as Age. The candidate should use Cache-Control, Date, Age, validators, status codes, and structured metrics instead of free-form client contracts.
Clarifying questions to ask first
- Is Warning generated by the origin, a shared cache, or an edge gateway, and are there multiple proxy layers?
- Are 110 and 111 diagnostics only, or do clients change business behavior because of them?
- Do responses include
Date,Age,Cache-Control,ETag, andLast-Modified? - Which legacy clients must be supported, and can the systems dual-write temporarily?
- Who needs “fresh,” “stale,” and “revalidation failed”: users, business logic, or operators?
A 30-second answer framework
“I would treat Warning as historical cache diagnostics, not a stable user-error contract. 110 described a stale response and 111 a failed revalidation, but the field is deprecated and unsuitable for free-form text. I would inventory consumers and proxy layers, express facts with Cache-Control, Date, Age, validators, and structured metrics, dual-write only for a bounded compatibility window, then remove Warning after observing client behavior. Cache hit rate, stale serving, revalidation failure, and error-budget metrics would verify the migration.”
Step-by-step deep answer
Step 1: Recover Warning's historical role
Warning was a request and response header containing a three-digit code, the generating agent, and text. Cache-related 110 and 111 described a stale response and failed revalidation; they were not HTTP status codes and did not mean 4xx or 5xx. Multiple proxies could append fields, making free-form text's source and trust unstable.
Step 2: Explain why migration is needed
MDN marks Warning as deprecated because it was not broadly generated or surfaced and the relevant standards no longer recommend this general warning mechanism. Relying on it binds cache diagnostics to unstable text and makes proxy duplication, reordering, or filtering hard to reason about. First prove that no client treats it as a business signal.
Step 3: Express facts with standard cache fields
Date identifies response generation time, while Age approximates residence time in a shared cache. Cache-Control defines freshness and revalidation requirements, and ETag or Last-Modified supports conditional requests. Together they describe cache state; a custom X-Warning string is not a substitute. The origin must also set Vary correctly so representations for different requests are not mixed.
Step 4: Move diagnostics into observability
The gateway should record structured events such as cache_status=stale, revalidation=failed, upstream=timeout, cache layer, and trace ID. Logs should avoid sensitive URL query values; metrics should aggregate by route, cache-key family, and upstream error class. The response should carry only user-relevant state, while operational detail stays in controlled systems.
Step 5: Design a compatibility dual-write window
Stop consuming Warning on shadow traffic or a small route first, checking SDKs, scripts, and proxy rules for dependencies. If a legacy client exists, keep Warning briefly while publishing a replacement field or upgrade guide. The replacement needs a stable enum and versioned contract, not copied arbitrary text. Give dual-write an explicit sunset date.
Step 6: Handle layered caches and validation failures
A failed revalidation does not necessarily mean the origin is down; it can be a timeout, rejected conditional request, or upstream 5xx. Decide whether to serve stale content, return an error, or apply a stale-if-error policy, and record the reason. Removing Warning must not hide stale serving or create infinite retries.
Step 7: Separate 103 Early Hints from cache warnings
103 Early Hints precedes the final response and hints at likely resources, commonly through Link for preconnect or preload. It does not report cache freshness or revalidation failure and does not replace Age or Cache-Control. State the timing, consumer, and failure semantics of each field instead of treating every hint as a Warning.
Step 8: Remove it in stages and verify
Compare Warning reads, cache hit rate, stale-serving ratio, conditional-request success, origin errors, P95 latency, and client errors before and after migration. Disable generation in one cache layer, expand across the chain, then remove code, documentation, and alerts after legacy clients are clean. A rollback switch should restore compatibility output only, never restore business dependence on Warning.
Trade-offs and boundaries
Keeping Warning has low short-term compatibility cost but prolongs an unstable contract and ambiguous troubleshooting. Immediate removal reduces protocol noise but can break legacy scripts. The safest path observes real dependencies, then moves the need to standard cache fields and structured observability.
Do not interpret Age as origin generation time or Cache-Control: no-cache as “do not cache”; it requires revalidation before reuse. Cache policy, validators, and error responses must be designed with the business's acceptable freshness window.
Rollout plan and evidence
In week one, export Warning read paths from edge, shared caches, and clients, establishing a baseline by route and cache layer. Next, dual-write structured metrics on a route without critical dependency and compare Age, conditional requests, and error rates. Finally, disable Warning layer by layer while keeping a one-click compatibility-output switch.
Acceptance requires no production client reads Warning; cache-hit and revalidation success do not regress; stale-if-error events are explainable; logs and metrics correlate by trace ID; and docs, SDKs, and alerts are updated.
Common pitfalls and follow-ups
Treating 110 as a 4xx or 5xx
110 is a historical Warning code, not a response status. Business errors belong in status and response contracts; cache diagnostics belong in metrics.
Recreating the problem with X-Warning
Free text, proxy appends, and version compatibility remain uncertain. If compatibility is required, use a stable enum, version, and explicit consumers.
Removing the header without observability
Deleting the field does not fix stale serving or failed revalidation. Establish cache-state metrics, tracing, and alerts before removing the external header.
Treating 103 Early Hints as a cache warning
103's timing and purpose are resource hints; it cannot express freshness or revalidation failure. Keep separate fields and processing paths.
How do you prove it can be removed?
Observe client reads, proxy configuration, error rates, and cache metrics; run a bounded dual-write window and expand after a single-layer canary. Do not delete globally without dependency evidence.