concept7 / laravel-forduty
Point browsers at your for.duty ingest endpoint with a Reporting-Endpoints header.
Fund package maintenance!
Requires
- php: ^8.3
- laravel/framework: ^13.0
Requires (Dev)
- larastan/larastan: ^3.9
- laravel/pao: ^1.0
- laravel/pint: ^1.29
- orchestra/testbench: ^11.0
- pestphp/pest: ^4.6
- pestphp/pest-plugin-laravel: ^4.1
- pestphp/pest-plugin-type-coverage: ^4.0
- phpstan/extension-installer: ^1.4
This package is auto-updated.
Last update: 2026-08-07 20:42:20 UTC
README
Laravel Forduty
for.duty collects your sites' browser reports — CSP violations, network errors, deprecations — groups them into distinct problems, and alerts your team only when something new appears.
Installation
You can install the package via Composer:
composer require concept7/laravel-forduty
You may publish all of the package's resources at once:
php artisan vendor:publish --tag="laravel-forduty"
Or, you may publish each resource individually:
Publishing the Configuration File
php artisan vendor:publish --tag="laravel-forduty-config"
Usage
Add your for.duty site token to your .env file:
FORDUTY_TOKEN=your-site-token
That's it. The package automatically appends its middleware to the global middleware stack, so every response your application returns carries a Reporting-Endpoints header pointing browsers at your for.duty endpoint:
Reporting-Endpoints: default="https://in.forduty.app/your-site-token"
The global stack is deliberate: a browser only delivers a report to an endpoint group it has been given on that same response, so anything the header misses reports nothing. Routes in the api group, routes that bring their own middleware list — a Filament panel, for one — and requests that match no route at all are all covered. Responses your application never sees, such as static files served by the web server, are not.
The middleware adds no header when the token is missing or blank, or when the base URL is blank, unparseable, not an absolute http or https URL, or carries credentials. That keeps a misconfiguration from breaking responses, and makes local and development environments quiet by default. The reporting URL defaults to https://in.forduty.app and can be overridden with FORDUTY_BASE_URL.
The middleware overwrites any existing Reporting-Endpoints header. To disable it for specific routes, use withoutMiddleware() — global middleware is normally beyond its reach, but this one checks the matched route before it writes the header:
use Concept7\LaravelForduty\Http\Middleware\AddReportingEndpointsHeader; Route::get('/embed', EmbedController::class) ->withoutMiddleware(AddReportingEndpointsHeader::class);
The package registers a second middleware, AddNetworkErrorLoggingHeader, which is dormant until you opt in — see Network Error Logging.
Opting Report Types In
Reporting-Endpoints only names the endpoints a browser is allowed to deliver reports to. Which reports actually get sent depends on the report type:
-
Deprecations, interventions and crashes are delivered to the
defaultendpoint on their own. The header above is all they need. -
CSP violations are only reported once your
Content-Security-Policyheader points at the endpoint with areport-todirective:Content-Security-Policy: default-src 'self'; report-to default -
Network errors require an additional
NELheader, which this package can send for you — see Network Error Logging.
CSP reporting is the one this package cannot turn on for you, because the directive belongs to a header it does not own. If you build your policy with a package such as spatie/laravel-csp, add the report-to default directive to it.
Network Error Logging
Network Error Logging asks the browser to report requests that failed before your application ever saw them — DNS failures, TCP resets, TLS errors, aborted connections. It is off by default. Enable it with:
FORDUTY_NEL_ENABLED=true
Responses then carry a policy alongside the endpoints header:
NEL: {"report_to":"default","max_age":2592000,"include_subdomains":false,"success_fraction":0,"failure_fraction":1}
Four optional variables tune it:
| Variable | Default | Meaning |
|---|---|---|
FORDUTY_NEL_MAX_AGE |
2592000 |
How long, in seconds, the browser keeps the policy. 0 clears a policy it already holds. |
FORDUTY_NEL_INCLUDE_SUBDOMAINS |
false |
Whether the policy also covers subdomains. |
FORDUTY_NEL_SUCCESS_FRACTION |
0.0 |
Fraction of successful requests to report. Raise this only deliberately — at 1.0 the browser reports every request your site makes. |
FORDUTY_NEL_FAILURE_FRACTION |
1.0 |
Fraction of failed requests to report. Lower this to sample on a high-traffic site. |
Two things to know:
- Browsers only honour
NELover HTTPS. Over plainhttpthe header is sent and ignored, so local development stays quiet on its own. - The policy needs
Reporting-Endpointson the same responses.report_tonames thedefaultgroup that the other middleware declares, so excludingAddReportingEndpointsHeaderwhile keeping this one leaves the browser with a policy it cannot deliver to.
As with the endpoints header, a value a browser would reject — a negative or fractional max_age, a sampling fraction outside 0.0–1.0 — means no header at all rather than a silently corrected one. To disable it per route:
use Concept7\LaravelForduty\Http\Middleware\AddNetworkErrorLoggingHeader; Route::get('/embed', EmbedController::class) ->withoutMiddleware(AddNetworkErrorLoggingHeader::class);
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Thank you for considering contributing to Laravel Forduty! Please review our contributing guide to get started.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
Laravel Forduty is open-sourced software licensed under the MIT license.