cachedcert

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 2, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

README

caddy-get-certificate-cache

Go Reference CI Go Report Card License

A Caddy module that caches get_certificate results in-process, so externally-provided TLS certificates are served from memory instead of being re-fetched over HTTP on every handshake.

Why

Caddy's certificate manager library, certmagic, does not cache the result of a get_certificate manager: by design it calls the manager on every TLS handshake (see caddyserver/caddy#7038). For certificates fetched from an HTTP endpoint via the stock tls.get_certificate.http getter, that means a backend round trip on every handshake, even though the certificate rarely changes.

This module is a drop-in alternative that wraps the same HTTP fetch with an in-process cache. The hot path becomes a map lookup (microseconds) instead of an HTTP request.

Module ID

tls.get_certificate.cached_http (implements certmagic.Manager).

Install

Build Caddy with this module using xcaddy:

xcaddy build --with github.com/ohdearapp/caddy-get-certificate-cache

To build against a local checkout (for development), point the module at it with a replace:

git clone https://github.com/ohdearapp/caddy-get-certificate-cache.git
xcaddy build --with github.com/ohdearapp/caddy-get-certificate-cache=./caddy-get-certificate-cache

Because the module is compiled into Caddy, upgrading Caddy means re-running xcaddy with the module included.

Configuration

Caddyfile
tls {
    get_certificate cached_http https://app.example.com/cert {
        ttl          1h
        negative_ttl 60s
        cache_dir    /var/cache/caddy-certs
    }
    on_demand
}
Option Type Default Description
(argument) URL — (required) Endpoint queried on a cache miss. server_name, signature_schemes, cipher_suites (and local_ip when available) are added as query parameters, identical to the stock http getter.
ttl duration from response How long a fetched certificate is cached. When omitted, the s-maxage of the response's Cache-Control header is used, falling back to 60s.
negative_ttl duration 60s How long a 204 ("no certificate for this name") response is remembered before re-querying.
cache_dir path (disabled) If set, fetched certificate bundles are persisted here (0600 files in a 0700 directory) and loaded on startup, so the cache survives restarts. Contains private keys.
Behaviour
  • HTTP 200: response body is parsed as a PEM bundle (certificate chain + private key) and cached.
  • HTTP 204: cached as a negative entry for negative_ttl; the handshake falls through to Caddy's on-demand / ask flow.
  • Any other status, or a parse error: returned as an error and not cached, so a transient upstream failure is retried on the next handshake.
  • Concurrent cache misses for the same name are coalesced into a single upstream request.
  • Each upstream fetch is bounded by a 10s timeout, and it does not inherit the per-handshake context, so one client disconnecting cannot cancel a fetch that other handshakes are waiting on.
  • Entries loaded from cache_dir on startup are re-aged: they get a fresh TTL on load (the original Cache-Control is not persisted), so a warm-restart entry re-validates within one TTL.

Running the tests

go test ./...
go test -race ./...   # what CI runs
gofmt -l .            # must print nothing
go vet ./...

Requires Go 1.25 or newer.

Deployment notes

  • Point cache_dir at a directory only the Caddy user can read; it stores private keys. The 0700 mode is applied only when the module creates the directory.
  • Keep the previous Caddy binary around for instant rollback after swapping in a build that includes this module.

License

Apache-2.0.

Documentation

Overview

Package cachedcert provides a Caddy tls.get_certificate module that caches certificates fetched over HTTP, so they are served from memory on subsequent TLS handshakes instead of re-fetched every time.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CachedHTTPGetter

type CachedHTTPGetter struct {
	// URL is the endpoint queried on a cache miss. The certificate-providing
	// server_name (and the handshake's signature_schemes / cipher_suites) are
	// added as query parameters, identical to the stock http getter.
	URL string `json:"url,omitempty"`

	// TTL, when set, is how long a fetched certificate is cached. When zero,
	// the s-maxage of the response's Cache-Control header is used, falling back
	// to defaultTTL.
	TTL caddy.Duration `json:"ttl,omitempty"`

	// NegativeTTL is how long a 204 (no certificate) response is cached. When
	// zero, defaultNegativeTTL is used.
	NegativeTTL caddy.Duration `json:"negative_ttl,omitempty"`

	// CacheDir, when set, persists fetched certificate bundles to disk so the
	// cache survives a restart. Files are written 0600 in a 0700 directory.
	// The 0700 mode is applied only when this module creates the directory; if
	// you pre-create it, restrict its permissions yourself, as it holds private keys.
	CacheDir string `json:"cache_dir,omitempty"`
	// contains filtered or unexported fields
}

CachedHTTPGetter is a certmagic.Manager that fetches certificates from an HTTP endpoint (like Caddy's stock tls.get_certificate.http) and caches the result in memory, optionally persisting it to disk for warm restarts.

Example

ExampleCachedHTTPGetter shows the configuration the module exposes. In practice it is registered as the Caddy module tls.get_certificate.cached_http and configured through the Caddyfile rather than constructed directly.

getter := &CachedHTTPGetter{
	URL:         "https://app.example.com/cert",
	TTL:         caddy.Duration(time.Hour),
	NegativeTTL: caddy.Duration(time.Minute),
	CacheDir:    "/var/cache/caddy-certs",
}
_ = getter

func (*CachedHTTPGetter) CaddyModule

func (*CachedHTTPGetter) CaddyModule() caddy.ModuleInfo

CaddyModule returns the Caddy module information.

func (*CachedHTTPGetter) GetCertificate

func (g *CachedHTTPGetter) GetCertificate(ctx context.Context, hello *tls.ClientHelloInfo) (*tls.Certificate, error)

GetCertificate implements certmagic.Manager. It is called on every TLS handshake, so the hot path is a map lookup; only a miss performs HTTP.

func (*CachedHTTPGetter) Provision

func (g *CachedHTTPGetter) Provision(ctx caddy.Context) error

Provision sets up the module.

func (*CachedHTTPGetter) UnmarshalCaddyfile

func (g *CachedHTTPGetter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

UnmarshalCaddyfile parses the get_certificate cached_http directive:

cached_http <url> {
    ttl          <duration>
    negative_ttl <duration>
    cache_dir    <path>
}

func (*CachedHTTPGetter) Validate

func (g *CachedHTTPGetter) Validate() error

Validate ensures the module is configured correctly.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL