Skip to content
Back to skills

Br Etag Cache

ASecurity

Add better-route 1.1 ETag and If-None-Match handling to GET or HEAD routes. Use for ETagMiddleware, strong or weak validators, custom etagResolver, WP_REST_Response preservation, comma-separated validators, wildcard matching, 304 responses, Cache-Control, proxy-stripped ETag troubleshooting, reviewing conditional HTTP caching, or configuring identity/namespace/URL isolation in CachingMiddleware. The middleware skips WP_Error, 204, redirects, and non-2xx responses.

  • 22 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added June 5, 2026
developmentjavascriptjavaphpbashapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 21, 2026

npx -y skills add Lonsdale201/wp-agent-skills --skill br-etag-cache --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Br Etag Cache?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Br Etag Cache
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lonsdale201-br-etag-cache/badge)](https://www.skillsdirectory.com/skills/lonsdale201-br-etag-cache)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: br-etag-cache
description: Add better-route 1.1 ETag and If-None-Match handling to GET or HEAD routes. Use for ETagMiddleware, strong or weak validators, custom etagResolver, WP_REST_Response preservation, comma-separated validators, wildcard matching, 304 responses, Cache-Control, proxy-stripped ETag troubleshooting, reviewing conditional HTTP caching, or configuring identity/namespace/URL isolation in CachingMiddleware. The middleware skips WP_Error, 204, redirects, and non-2xx responses.
metadata:
  wp-skills-author: "Soczó Kristóf"
  wp-skills-contact: "mailto:lonsdale201@hotmail.com"
  wp-skills-plugin: "better-route"
  wp-skills-plugin-version-tested: "1.1.1"
  wp-skills-php-min: "8.1"
  wp-skills-last-updated: "2026-09-21"
---

# better-route: ETag conditional reads

Use ETags on read routes to let clients revalidate a representation. They do not prevent duplicate writes; use idempotency for that.

```php
use BetterRoute\Middleware\Cache\ETagMiddleware;

$router->get('/catalog', $handler)
    ->publicRoute()
    ->middleware([new ETagMiddleware()]);
```

The default validator is a quoted SHA-1 of the JSON-encoded response body. It applies only to `GET`/`HEAD` results with status 200–299 except 204.

## 1.1 behavior

The middleware preserves Better Route `Response` and `WP_REST_Response` status/data. It adds the ETag through the appropriate response API instead of flattening the WordPress response.

It skips:

- returned `WP_Error`;
- non-2xx responses;
- `204 No Content`;
- non-GET/HEAD methods.

`If-None-Match` accepts:

- a single validator;
- a comma-separated validator list;
- weak or strong forms of the same opaque tag;
- `*`.

On a match, 1.1 returns `304` with no body and preserves cache-relevant source headers: `Cache-Control`, `Content-Location`, `Expires`, and `Vary`, plus the ETag.

The middleware computes and controls the final `ETag` header; do not rely on an existing handler ETag remaining unchanged.

## Cheap custom validators

For large responses, derive a validator from a stable version instead of hashing the full body:

```php
use BetterRoute\Http\RequestContext;

$etag = new ETagMiddleware(
    weak: false,
    etagResolver: static function (mixed $response, RequestContext $context): string {
        return (string) get_option('myapp_catalog_version', 0);
    },
);
```

Return the opaque value; the middleware quotes it. A returned already-quoted or `W/` value is normalized. Invalid quote/control bytes are replaced with a safe hash rather than reaching an HTTP header.

Use `weak: true` when byte differences may represent the same semantic representation:

```php
new ETagMiddleware(weak: true); // W/"..."
```

The default JSON hash follows array order. Deeply sort associative data before returning it, or use a stable version resolver, when construction order is nondeterministic.

## Cache-Control and privacy

ETag enables revalidation; it does not define freshness or sharing. Set Cache-Control separately:

```php
return new Response($data, 200, [
    'Cache-Control' => 'public, max-age=300',
]);
```

Use `private`/`no-store` as appropriate for user-specific data. Never let a shared CDN cache `/me` or another personalized URL merely because it has an ETag.

## Server response-cache scope (1.1.1)

`CachingMiddleware` is separate from ETag revalidation. Place authentication before its cache lookup. Default keys include the router namespace, route template, concrete request path, separate URL parameters, identity and merged request parameters. Query/body fields cannot shadow a URL ID into another target's cache entry.

Keep `RequestContext::routePath` as the template; the router supplies `attributes['routeNamespace']`. Custom key resolvers own equivalent namespace/URL/identity/tenant isolation. Test same-template routes across namespaces and different URL IDs with identical shadowing query values.

Old cache entries become cold and expire naturally. For idempotency, the same key-scope fix requires coordinated deployment; follow `br-install-and-migrate` rather than treating write replay like disposable read caching. Rate-limit buckets and optimistic-lock scope are unchanged.

## Troubleshooting

Test both the public endpoint and the PHP/upstream origin. A reverse proxy, nginx/RunCloud rule, CDN, compression layer, or caching plugin may remove or rewrite an outbound ETag even when application-level matching still produces a correct 304.

Verify:

```bash
curl -i 'https://example.com/wp-json/myapp/v1/catalog'
curl -i 'https://example.com/wp-json/myapp/v1/catalog' \
  -H 'If-None-Match: "copied-tag"'
```

Also test `If-None-Match: W/"copied-tag"`, a comma-separated list, and `*`.

## Review checklist

- Attach only to GET/HEAD routes.
- Use a cheap stable resolver for large bodies.
- Set explicit Cache-Control and correct privacy semantics.
- Confirm WP REST response status/data/headers survive.
- Confirm 4xx/5xx and WP_Error do not gain an ETag.
- Confirm matched 304 has no body and retains cache headers.
- Inspect intermediary header behavior if ETag disappears externally.

## Related skills

- Use `br-idempotency` or `br-atomic-idempotency` for write retries.
- Use `br-cors-public-client` to expose `ETag` to browser JavaScript.

## References

- Verified source paths:
  - `src/Middleware/Cache/ETagMiddleware.php`
  - `src/Http/Response.php`

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.