httpcache is a Go package that provides a standards-compliant http.RoundTripper for transparent HTTP response caching, following RFC 9111 (HTTP Caching).
Note: This package is intended for use as a private (client-side) cache. It is not a shared or proxy cache. It is designed to be used with an HTTP client to cache responses from origin servers, improving performance and reducing load on those servers.
- Plug-and-Play: Just swap in as your HTTP client's transport; no extra configuration needed.1
- RFC 9111 Compliance: Handles validation, expiration, and revalidation (view full compliance details).
- Cache Control: Supports all required HTTP cache control directives, as well as extensions like
stale-while-revalidate
,stale-if-error
, andimmutable
(view details). - Cache Backends: Built-in support for file system and memory caches, with the ability to implement custom backends (see Cache Backends).
- Extensible: Options for logging, transport and timeouts (see Options).
- Debuggable: Adds a cache status header to every response (see Cache Status Header).
- Zero Dependencies: No external dependencies, pure Go implementation.
Demonstration of HTTP caching in action. See _examples/app for code.
To install the package, run:
go get github.com/bartventer/httpcache
To get started, create a new HTTP client with the httpcache
transport, specifying a cache backend DSN. You'll need to register the desired cache backend before using it. Here's an example using the built-in file system cache:
package main
import (
"log/slog"
"net/http"
"time"
"github.com/bartventer/httpcache"
// Register the file system cache backend
_ "github.com/bartventer/httpcache/store/fscache"
)
func main() {
// Example DSN for the file system cache backend
dsn := "fscache://?appname=myapp"
client := &http.Client{
Transport: httpcache.NewTransport(
dsn,
httpcache.WithSWRTimeout(10*time.Second),
httpcache.WithLogger(slog.Default()),
),
}
// ... Use the client as usual
}
Note: The DSN format and options depend on the cache backend you choose. Refer to the Cache Backends section for details on available backends and their DSN formats.
Backend | DSN Example | Description |
---|---|---|
fscache |
fscache://?appname=myapp |
Built-in file system cache, stores responses on disk |
memcache |
memcache:// |
Built-in memory cache, stores responses in memory |
Consult the documentation for each backend for specific configuration options and usage details.
To implement a custom cache backend, create a type that satisfies the store.Cache
interface, then register it using the store.Register
function. Refer to the built-in backends for examples of how to implement this interface.
Option | Description | Default Value |
---|---|---|
WithTransport(http.RoundTripper) |
Set the underlying transport | http.DefaultTransport |
WithSWRTimeout(time.Duration) |
Set the stale-while-revalidate timeout | 5 * time.Second |
WithLogger(*slog.Logger) |
Set a logger for debug output | slog.New(slog.DiscardHandler) |
Every response includes a cache status header to indicate how the response was served. The header is named X-Httpcache-Status
and can have the following values:
Status | Description |
---|---|
HIT |
Served from cache |
MISS |
Fetched from origin |
STALE |
Served stale from cache |
REVALIDATED |
Revalidated with origin |
BYPASS |
Cache bypassed |
X-Httpcache-Status: HIT
-
Range Requests & Partial Content: This cache does not support HTTP range requests or partial/incomplete responses (e.g., status code 206,
Range
/Content-Range
headers). All requests with aRange
header are bypassed, and 206 responses are not cached. For example:GET /example.txt HTTP/1.1 Host: example.com Range: bytes=0-99
The above request will bypass the cache and fetch the response directly from the origin server. See RFC 9111 §3.3-3.4 for details.
§ | Title | Requirement | Implemented | Notes |
---|---|---|---|---|
1. | Introduction | N/A | N/A | Nothing to implement |
2. | Overview of Cache Operation | N/A | N/A | Nothing to implement |
3. | Storing Responses in Caches | Required | ✔️ | Details |
4. | Constructing Responses from Caches | Required | ✔️ | Details |
5. | Field Definitions | Required | ✔️ | Details |
6. | Relationship to Applications and Other Caches | N/A | N/A | Nothing to implement |
7. | Security Considerations | N/A | N/A | Nothing to implement |
8. | IANA Considerations | N/A | N/A | Nothing to implement |
9. | References | N/A | N/A | Nothing to implement |
Legend for Requirements:
Requirement | Description |
---|---|
Required | Must be implemented for RFC compliance |
Optional | May be implemented, but not required for compliance |
Obsolete | Directive is no longer relevant as per RFC 9111 |
Deprecated | Directive is deprecated as per RFC 9111, but can still be implemented |
N/A | Nothing to implement or not applicable to private caches |
§3. Storing Responses in Caches (Details)
§ | Title | Requirement | Implemented | Notes |
---|---|---|---|---|
3.1. | Storing Header and Trailer Fields | Required | ✔️ | |
3.2. | Updating Stored Header Fields | Required | ✔️ | |
3.3. | Storing Incomplete Responses | Optional | ❌ | See Limitations |
3.4. | Combining Partial Content | Optional | ❌ | See Limitations |
3.5. | Storing Responses to Authenticated Requests | N/A | N/A | Not applicable to private client-side caches |
§4. Constructing Responses from Caches (Details)
§ | Title | Requirement | Implemented | Notes |
---|---|---|---|---|
4.1. | Calculating Cache Keys with the Vary Header Field | Required | ✔️ | |
4.2. | Freshness | Required | ✔️ | Details |
§4.2. Freshness (Subsections)
§ | Title | Requirement | Implemented | Notes |
---|---|---|---|---|
4.2.1. | Calculating Freshness Lifetime | Required | ✔️ | |
4.2.2. | Calculating Heuristic Freshness | Required | ✔️ | |
4.2.3. | Calculating Age | Required | ✔️ | |
4.2.4. | Serving Stale Responses | Required | ✔️ |
§ | Title | Requirement | Implemented | Notes |
---|---|---|---|---|
4.3. | Validation | Required | ✔️ | Details |
§4.3. Validation (Subsections)
§ | Title | Requirement | Implemented | Notes |
---|---|---|---|---|
4.3.1. | Sending a Validation Request | Required | ✔️ | |
4.3.2. | Handling Received Validation Request | N/A | N/A | Not applicable to private client-side caches |
4.3.3. | Handling a Validation Response | Required | ✔️ | |
4.3.4. | Freshening Stored Responses upon Validation | Required | ✔️ | |
4.3.5. | Freshening Responses with HEAD | Optional | ❌ | Pointless, rather use conditional GETs; see RFC 9110 §13.2.1 last para |
§ | Title | Requirement | Implemented | Notes |
---|---|---|---|---|
4.4. | Invalidating Stored Responses | Required | ✔️ |
§5. Field Definitions (Details)
§ | Title | Requirement | Implemented | Notes |
---|---|---|---|---|
5.1. | Age | Required | ✔️ | |
5.2. | Cache-Control | Required | ✔️ | Details |
5.3. | Expires | Required | ✔️ | |
5.4. | Pragma | Deprecated | ❌ | Deprecated by RFC 9111; not implemented |
5.5. | Warning | Obsolete | ❌ | Obsoleted by RFC 9111; not implemented |
§5.2. Cache-Control Directives
§ | Title | Requirement | Implemented | Notes |
---|---|---|---|---|
5.2.1. | Request Directives | Optional | ✔️ | Details |
§5.2.1. Request Directives (Details)
§ | Title/Directive | Requirement | Implemented | Notes |
---|---|---|---|---|
5.2.1.1. | max-age |
Optional | ✔️ | |
5.2.1.2. | max-stale |
Optional | ✔️ | |
5.2.1.3. | min-fresh |
Optional | ✔️ | |
5.2.1.4. | no-cache |
Optional | ✔️ | |
5.2.1.5. | no-store |
Optional | ✔️ | |
5.2.1.6. | no-transform |
Optional | ✔️ | Compliant by default - implementation never transforms content |
5.2.1.7. | only-if-cached |
Optional | ✔️ |
Section | Requirement | Implemented | Notes |
---|---|---|---|
5.2.2. Response Directives | Required | ✔️ | Details |
§5.2.2. Response Directives (Details)
§ | Title/Directive | Requirement | Implemented | Notes |
---|---|---|---|---|
5.2.2.1. | max-age |
Required | ✔️ | |
5.2.2.2. | must-revalidate |
Required | ✔️ | |
5.2.2.3. | must-understand |
Required | ✔️ | |
5.2.2.4. | no-cache |
Required | ✔️ | Both qualified and unqualified forms supported |
5.2.2.5. | no-store |
Required | ✔️ | |
5.2.2.6. | no-transform |
Required | ✔️ | Compliant by default - implementation never transforms content |
5.2.2.7. | private |
N/A | N/A | Intended for shared caches; not applicable to private caches |
5.2.2.8. | proxy-revalidate |
N/A | N/A | Intended for shared caches; not applicable to private caches |
5.2.2.9. | public |
Optional | ✔️ | |
5.2.2.10. | s-maxage |
N/A | N/A | Intended for shared caches; not applicable to private caches |
§ | Title | Requirement | Implemented | Notes |
---|---|---|---|---|
5.2.3. | Extension Directives | Optional | partially | Details |
§5.2.3. Extension Directives (Details)
The following additional cache control directives are supported, as defined in various RFCs:
Reference | Directive | Notes |
---|---|---|
RFC 5861, §3 | stale-while-revalidate |
Only applies to responses |
RFC 5861, §4 | stale-if-error |
Applies to both requests and responses |
RFC 8246, §2 | immutable |
Only applies to responses |
This project is licensed under the Apache License 2.0. See the LICENSE file for details.
Footnotes
-
While no configuration is needed, you can customize the underlying transport.
httpcache
wraps your transport (default:http.DefaultTransport
) to add caching with minimal code changes. ↩