PHPackages                             expertsystemsau/laravel-conditional-requests - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. expertsystemsau/laravel-conditional-requests

ActiveLibrary[API Development](/categories/api)

expertsystemsau/laravel-conditional-requests
============================================

HTTP conditional requests for Laravel — ETag, Last-Modified, 304 Not Modified, and lost-update protection with If-Match.

00PHPCI passing

Since Aug 25Pushed todayCompare

[ Source](https://github.com/expertsystemsau/laravel-conditional-requests)[ Packagist](https://packagist.org/packages/expertsystemsau/laravel-conditional-requests)[ RSS](/packages/expertsystemsau-laravel-conditional-requests/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (1)Used By (0)

Laravel Conditional Requests
============================

[](#laravel-conditional-requests)

**HTTP conditional requests for Laravel — ETag, Last-Modified, 304 Not Modified, and lost-update protection with `If-Match`.**

 [![Packagist](https://camo.githubusercontent.com/24b9738ac986128f74c6808ce800373cef26f6e9f3197dfa85f2a531c812c48d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f65787065727473797374656d7361752f6c61726176656c2d636f6e646974696f6e616c2d72657175657374732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/expertsystemsau/laravel-conditional-requests) [![PHP from Packagist](https://camo.githubusercontent.com/11bdbb3ea15e07dbfd8360d9cb4c824a241b9ff751285d953d35828db927d055/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f65787065727473797374656d7361752f6c61726176656c2d636f6e646974696f6e616c2d72657175657374732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/expertsystemsau/laravel-conditional-requests) [![Laravel versions](https://camo.githubusercontent.com/9c780a374fbb9c9aa534e2e0d6e2d78d64f8b661c7cd9c61a30f6d5e2c6adfef/68747470733a2f2f62616467652e6c61726176656c2e636c6f75642f62616467652f65787065727473797374656d7361752f6c61726176656c2d636f6e646974696f6e616c2d72657175657374733f7374796c653d666c6174)](https://packagist.org/packages/expertsystemsau/laravel-conditional-requests) [![GitHub Workflow Status (main)](https://camo.githubusercontent.com/2998a6033fe5f34969aa09552052be8b86bba17c41a9039b318486562391e2ac/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f65787065727473797374656d7361752f6c61726176656c2d636f6e646974696f6e616c2d72657175657374732f74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d5465737473267374796c653d666c61742d737175617265)](https://github.com/expertsystemsau/laravel-conditional-requests/actions) [![Total Downloads](https://camo.githubusercontent.com/8db0b063b15f2f0e7748899b5df287d83fb18abaa76e5bbdb53c4c98bfd3e100/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f65787065727473797374656d7361752f6c61726176656c2d636f6e646974696f6e616c2d72657175657374732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/expertsystemsau/laravel-conditional-requests) [![License](https://camo.githubusercontent.com/302da36e48ed308b148b03b8bc417f01f63b2901a8a91e87c8dac47e98cb31cf/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f65787065727473797374656d7361752f6c61726176656c2d636f6e646974696f6e616c2d72657175657374732e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)

Conditional requests are the part of HTTP that lets a client and a server agree on *which version* of a resource they are talking about, before either of them acts on it. [RFC 9110 §13](https://www.rfc-editor.org/rfc/rfc9110#section-13) defines them; ETag and `Last-Modified` are just the two validators underneath.

They solve two different problems with the same handshake:

- **Reads** — the client already holds version `v7`. If nothing has changed, it should get an empty `304 Not Modified` instead of the payload again. Less bandwidth, less serialization, faster clients.
- **Writes** — the client read version `v7`, edited it, and is now sending it back. If someone else saved `v8` in between, the write must be **refused**, not silently applied over the top. This is the lost update problem, and `If-Match` is the fix.

Most Laravel packages in this space only do the first half, and only via ETag. This one treats conditional requests as the feature and validators as an implementation detail.

Status
------

[](#status)

Warning

**Pre-release — under active development.** The package skeleton, CI, and support matrix are in place; the middleware described under [Usage](#usage) is the design contract being built against and is **not implemented yet**. Nothing here is stable until `v1.0.0`. Watch the repo or the [changelog](CHANGELOG.md) for progress.

Requirements
------------

[](#requirements)

- PHP 8.3, 8.4, or 8.5
- Laravel 12.x or 13.x

Installation
------------

[](#installation)

Install the package via Composer:

```
composer require expertsystemsau/laravel-conditional-requests
```

The service provider is auto-discovered. No further setup is required to get started.

### Publishing Resources

[](#publishing-resources)

You may publish all of the package's resources at once:

```
php artisan vendor:publish --tag="laravel-conditional-requests"
```

Or publish each resource individually:

```
php artisan vendor:publish --tag="laravel-conditional-requests-config"
php artisan vendor:publish --tag="laravel-conditional-requests-lang"
php artisan vendor:publish --tag="laravel-conditional-requests-assets"
```

Usage
-----

[](#usage)

Note

This section documents the **intended API**. It is the design contract for `v1.0.0` and is not yet implemented — see [Status](#status).

### Conditional reads

[](#conditional-reads)

Apply the middleware to any route that returns a cacheable representation. The response gets a validator attached, and matching subsequent requests short-circuit to `304 Not Modified` with an empty body.

```
Route::get('/articles/{article}', ShowArticle::class)
    ->middleware('conditional');
```

```
GET /articles/42
→ 200 OK
  ETag: "d41d8cd98f00b204"

GET /articles/42
If-None-Match: "d41d8cd98f00b204"
→ 304 Not Modified          # no body, no serialization
```

### Conditional writes (lost update protection)

[](#conditional-writes-lost-update-protection)

Require the client to state which version it believes it is modifying. A stale token is rejected with `412`; a missing one with `428`.

```
Route::patch('/articles/{article}', UpdateArticle::class)
    ->middleware('conditional:required');
```

```
PATCH /articles/42
If-Match: "d41d8cd98f00b204"
→ 200 OK                    # still current, write applied

PATCH /articles/42
If-Match: "d41d8cd98f00b204"
→ 412 Precondition Failed   # someone else got there first

PATCH /articles/42
                            # no If-Match header at all
→ 428 Precondition Required
```

`428` is the piece most implementations skip. Without it a client can simply omit the header and go straight back to clobbering other people's writes — the protection is opt-out by default. `conditional:required` makes it opt-in-by-force for the routes you choose.

### Header reference

[](#header-reference)

Request headerApplies toOn matchOn mismatch`If-None-Match`reads`304 Not Modified``200 OK` with body`If-Modified-Since`reads`304 Not Modified``200 OK` with body`If-Match`writeswrite proceeds`412 Precondition Failed``If-Unmodified-Since`writeswrite proceeds`412 Precondition Failed`*(absent)*writes, when required—`428 Precondition Required`Roadmap
-------

[](#roadmap)

- `conditional` middleware — response validators and `304` short-circuiting
- `conditional:required` middleware — `If-Match` enforcement with `412` / `428`
- Strong and weak ETag generation, with a configurable strategy
- `Last-Modified` / `If-Modified-Since` support alongside ETags
- Model-derived validators, so an ETag comes from the record's version rather than a hash of the rendered body
- Eloquent API Resource and resource collection support
- Configurable exclusions — methods, status codes, routes, and response sizes
- Laravel Octane safety, with no validator state leaking between requests
- Migration notes for projects coming from `werk365/etagconditionals`

Testing
-------

[](#testing)

```
composer test          # static analysis, lint, type coverage, and the test suite
composer test:unit     # Pest only
composer analyse       # PHPStan
composer lint          # Pint
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Thank you for considering contributing to Laravel Conditional Requests! Please review our [contributing guide](.github/CONTRIBUTING.md) to get started.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](.github/SECURITY.md) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [Mitchell Williams](https://github.com/mitchello77)
- [All Contributors](../../contributors)

Prior art: [`werk365/etagconditionals`](https://github.com/365Werk/etagconditionals) mapped out this territory for Laravel first, and is worth a look if you need something available today.

License
-------

[](#license)

Laravel Conditional Requests is open-sourced software licensed under the [MIT license](LICENSE.md).

###  Health Score

20

↑

LowBetter than 12% of packages

Maintenance65

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/44283103?v=4)[xsys](/maintainers/xsys)[@Xsys](https://github.com/Xsys)

---

Top Contributors

[![mitchello77](https://avatars.githubusercontent.com/u/9065203?v=4)](https://github.com/mitchello77 "mitchello77 (5 commits)")

---

Tags

apiconcurrency-controlconditional-requestsetagetagshttphttp-cachehttp-cachingif-matchif-modified-sinceif-none-matchlaravellaravel-packagelast-modifiedmiddlewarenot-modifiedoptimistic-lockingphprest-apirfc9110

### Embed Badge

![Health badge](/badges/expertsystemsau-laravel-conditional-requests/health.svg)

```
[![Health](https://phpackages.com/badges/expertsystemsau-laravel-conditional-requests/health.svg)](https://phpackages.com/packages/expertsystemsau-laravel-conditional-requests)
```

###  Alternatives

[exsyst/swagger

A php library to manipulate Swagger specifications

35816.5M7](/packages/exsyst-swagger)[lucasdotvin/laravel-soulbscription

A straightforward interface to handle subscriptions and features consumption.

709209.3k](/packages/lucasdotvin-laravel-soulbscription)[pimax/fb-messenger-php

Facebook Messenger Bot PHP API

313188.5k2](/packages/pimax-fb-messenger-php)[commercetools/commercetools-api-reference

6520.5k3](/packages/commercetools-commercetools-api-reference)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
