Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Etagconditionals Laravel Package

werk365/etagconditionals

werk365/etagconditionals adds Laravel middleware for HTTP conditional requests, generating ETags and honoring If-None-Match and If-Match headers to return 304 Not Modified or 412 Precondition Failed responses automatically.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install: composer require werk365/etagconditionals. The service provider is auto-discovered in Laravel; no manual registration in config/app.php is needed on modern versions.
  • Look here first: the package README's middleware-registration snippet, then the published config (php artisan vendor:publish --tag=etagconditionals if a config tag exists). The config is where you confirm the middleware alias name, whether it's globally registered, and any tag-generation strategy.
  • Register the middleware on the routes that need conditional handling. The usual approach is the route alias, e.g. Route::get('/articles/{article}', [ArticleController::class, 'show'])->middleware('etag');, or add it to the web group in app/Http/Kernel.php if every response should be revalidatable.
  • First use case — GET revalidation: attach the middleware to a GET /api/articles/{article} endpoint, produce an ETag for the representation (the package will emit one, or you set it explicitly with Laravel's native $response->setEtag(md5($payload))), and a client sending If-None-Match: "<same-tag>" gets a bare 304 Not Modified instead of the full body.
  • Second use case — optimistic concurrency: on PUT/PATCH/DELETE, the client sends If-Match: "<tag-it-last-saw>". If the resource changed in between, the package responds 412 Precondition Failed and the controller never runs. This is the pattern that saves you from lost updates without a version column.

Implementation Patterns

  • Separate read and write semantics. If-None-Match on GET/HEAD means "give me 304 if unchanged"; on PUT/POST/PATCH/DELETE it means "fail with 412 if it already exists". If-Match always means "fail with 412 if it doesn't match". Wire the middleware per-route so each verb gets the intended behaviour rather than one blanket global rule.
  • Derive the tag from the representation, not the row. md5(json_encode($resource)) over the serialized payload is the cheapest correct option; sha1 if you want to avoid the theoretical collision discussion in code review. Hashing $model->updated_at or $model->id is a common shortcut and a common bug — it produces the same tag for two different serializations (different with includes, different auth-scoped fields).
  • Generate the tag after the representation is final. If your API resource adds computed fields, or a later middleware rewrites the body (JSON pretty-print, compression), compute the ETag on the post-transform bytes. Middleware ordering matters: place the ETag middleware outside anything that mutates the body, or apply it inside the controller after building the response.
  • Pair with Cache-Control. An ETag without a cacheable Cache-Control (e.g. no-cache, private — which still permits revalidation, unlike no-store) buys you nothing, because clients won't store the representation to compare against. This is the single most common integration mistake.
  • Tenant/auth scoping. If the same URL returns different representations for different users, the ETag must differ too — otherwise user B's If-None-Match from user A's tag yields a false 304. Either incorporate the auth scope into the hash or let the tag fall out of the already-scoped payload.
  • Testing pattern. $this->getJson($url, ['If-None-Match' => $etag])->assertStatus(304) and $this->putJson($url, [], ['If-Match' => $staleTag])->assertStatus(412). Capture the tag from an initial getJson() response header rather than hardcoding it, so the test survives serializer changes.
  • Use with route model binding. The tag then identifies the exact version of the bound model, so a stale If-Match cleanly maps to a 412 without any controller-side checks.
  • Fallback plan. The package's actual surface area is small — a middleware plus tag helpers. If you hit a Laravel 11/12 incompatibility, the same behaviour is ~60 lines of middleware around Response::setEtag() / Response::isNotModified().

Gotchas and Tips

  • Weak vs strong validators. Tags prefixed with W/ are weak. RFC 7232 requires strong comparison for If-Match, so a weak tag will (correctly) never satisfy an If-Match precondition. If writes always return 412, check your tag generator isn't emitting W/"...".
  • If-Match: * means "succeed if the resource exists at all". Don't let a naive string comparison reject it.
  • Multiple tags. Both headers accept a comma-separated list, e.g. If-None-Match: "abc", "def". A middleware that only parses a single tag will misbehave when a client sends a list.
  • Vary is your responsibility. If the same URL yields different representations by Accept, Accept-Language, or Accept-Encoding, the ETag must vary with them — and so should the Vary header. Otherwise a shared cache serves the wrong tag to the wrong client, and you get spurious 304s.
  • Debugging loop. curl -i -H 'Accept: application/json' https://app.test/api/thing to get the tag and headers, then repeat with -H 'If-None-Match: "<tag>"'. Confirm the ETag is byte-identical across the two responses (a missing quote or a trailing newline in the hash is enough to break it). Watch out for proxies stripping ETag or 304 bodies in dev.
  • no-store kills revalidation. Check that a global middleware isn't overriding your Cache-Control.
  • Release age. Last tagged release is March 2023. Verify the constraint in composer.json against your Laravel version before committing; if it doesn't declare support, fork it or inline the middleware rather than fighting the dependency resolver.
  • Extension points. The middleware is the natural seam: subclass it to add logging of 304/412 hits (useful for proving the feature pays for itself), or to swap the tag strategy per-route via a route default. Prefer a response macro (Response::macro('etagFrom', ...)) over editing vendor code for tag generation.
  • Don't tag everything. Static-ish endpoints and endpoints already behind a CDN with its own validator gain little; start with detail/show routes and expensive list endpoints where you can measure bandwidth savings.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk