Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?


Failed to load latest commit information.
Latest commit message
Commit time
March 19, 2021 10:31
August 2, 2021 09:46
April 28, 2022 08:27
February 13, 2021 18:04
February 14, 2021 16:47
March 22, 2023 11:32
February 13, 2021 18:04
February 13, 2021 18:04
June 3, 2021 11:16

Laravel ETag & Conditionals

Latest Version on Packagist Total Downloads StyleCI Scrutinizer Code Quality Build Status

This package provides a set of middlewares to both set ETags and handle HTTP Conditional Requests.

Currently both If-None-Matched and If-Match are supported.

The package aims to provide the tools to provide better client-side caching when building an API with Laravel, as well as preventing mid-air collisions.

When using the package and enabling the middleware, your client (browser) will take care of handling the caching provided by the ETag and If-None-Match headers automatically.


Via Composer

$ composer require werk365/etagconditionals


You can either use the middleware group, automatically applying all available middleware (recommended if using an apiResource route for example), by setting the etag middleware, or apply the middlewares individually.

Currently available middleware:

  • setEtag
  • ifMatch
  • ifNoneMatch


Method: Any

This middleware will set the ETag header on your responses. The ETag header is equal to a md5 hash of $response->getContent(). HEAD requests are supported by transforming the request to a GET request and changing it back on the response.


Method: PATCH

This middleware will create a new request to the GET equivalent of the endpoint called and retrieve the current content. After this, a hash of the current content and the If-Match hash will be compared. If the hashes match, the PATCH request will be allowed through the middleware, but if there is no match, 412 will be returned.

Important Since the internal GET request created will also pass through enabled middleware, you might run in to some cases where this is causing issues. For example: if you have a middleware that changes the response body that was not applied to the response that the If-Match etag belongs to, this will result in non-matching hashes.

For this scenario, this middleware sets a X-From-Middleware: IfMatch header which you can use in other middleware to filter these requests. Please note that since this header could also be set by a client, it should never be used to skip anything important like auth middleware.


Method: GET|HEAD

This middleware will simply compare the submitted If-None-Match header to a newly created etag of the response. If there is no match, 200 is returned, with the new response in the case of a GET request. If the hashes are matching, 304 is returned with no content, allowing the browser to used cached content instead.

Comparison algorithms

By default, a weak comparison algorithm will be used for both the IfMatch and IfNoneMatch ETags. In practise this means that we simply strip any W/ tags from the ETag, so they can be compared to normal tags created in the middleware. This is to support cases where certain configurations automatically add the W/ tag to our supplied ETag.

This behaviour can be changed by either publishing the config file:

$ php artisan vendor:publish --provider="Werk365\EtagConditionals\EtagConditionalsServiceProvider"

And then changing the following values:

return [
    'if_match_weak' => env('IF_MATCH_WEAK', true),
    'if_none_match_weak' => env('IF_NONE_MATCH_WEAK', true),

Or by setting the ENV values above.

Defining custom ETags

The static method EtagConditionals::etagGenerateUsing() allows you to have full control over how your Etag is generated by passing a callback as argument. This means you can do simple things like returning an ETag using a different algorithm, or other custom solutions.

        EtagConditionals::etagGenerateUsing(function (\Symfony\Component\HttpFoundation\Response $response) {
            return hash('sha256', $response->getContent());
        EtagConditionals::etagGenerateUsing(function (\Illuminate\Http\Request $request, \Symfony\Component\HttpFoundation\Response $response) {
            return Cache::rememberForever('etag.'.$request->url(), function () use ($response) {
                 return md5($response->getContent());

Change log

Please see the changelog for more information on what has changed recently.


Feel free to create issues and submit pull requests. For any PR submitted, make sure it is covered by tests or include new tests.


If you discover any security related issues, please email author email instead of using the issue tracker.



Please see the license file for more information.