concept7/laravel-forduty

Point browsers at your for.duty ingest endpoint with a Reporting-Endpoints header.

Maintainers

Package info

github.com/concept7/laravel-forduty

pkg:composer/concept7/laravel-forduty

Transparency log

Fund package maintenance!

concept7

Statistics

Installs: 82

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.5.0 2026-08-08 22:36 UTC

README

Laravel Forduty

Packagist PHP from Packagist Laravel versions GitHub Workflow Status Total Downloads

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

Then register the middleware yourself. The package ships it but does not attach it anywhere, so your application decides which responses carry the header.

Registering the Middleware

Append AddReportingEndpointsHeader to the global middleware stack in bootstrap/app.php:

use Concept7\LaravelForduty\Http\Middleware\AddReportingEndpointsHeader;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->append(AddReportingEndpointsHeader::class);
})

Every response your application returns then 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 the recommendation for a reason: 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. Appending globally covers 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. Responses your application never sees, such as static files served by the web server, are beyond reach either way.

To limit the header to one middleware group instead:

->withMiddleware(function (Middleware $middleware): void {
    $middleware->web(append: [AddReportingEndpointsHeader::class]);
})

Or to a single route:

Route::get('/checkout', CheckoutController::class)
    ->middleware(AddReportingEndpointsHeader::class);

Behavior

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 keep it off specific routes, register it on the web group rather than globally and exclude those routes with withoutMiddleware():

use Concept7\LaravelForduty\Http\Middleware\AddReportingEndpointsHeader;

Route::get('/embed', EmbedController::class)
    ->withoutMiddleware(AddReportingEndpointsHeader::class);

withoutMiddleware() only reaches middleware the router gathers, so it has no effect on a middleware appended to the global stack. Route-level exclusions and a global registration are a choice between the two.

The package ships a second middleware, AddNetworkErrorLoggingHeader, which you register the same way once 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 default endpoint on their own. The header above is all they need.

  • CSP violations are only reported once your Content-Security-Policy header points at the endpoint with a report-to directive:

    Content-Security-Policy: default-src 'self'; report-to default
    
  • Network errors require an additional NEL header, 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 until you register AddNetworkErrorLoggingHeader alongside the other middleware:

use Concept7\LaravelForduty\Http\Middleware\AddNetworkErrorLoggingHeader;
use Concept7\LaravelForduty\Http\Middleware\AddReportingEndpointsHeader;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->append([
        AddReportingEndpointsHeader::class,
        AddNetworkErrorLoggingHeader::class,
    ]);
})

Every response that middleware sees then carries a policy alongside the endpoints header:

NEL: {"report_to":"default","max_age":2592000,"include_subdomains":false,"success_fraction":0,"failure_fraction":1}

Registering the middleware is the whole opt-in — there is no separate flag to set. To turn network error logging off again, take it back out of bootstrap/app.php. Browsers hold a policy for as long as its max_age says, though, so a site that has been sending one wants a deploy with FORDUTY_NEL_MAX_AGE=0 to clear it before the middleware goes.

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 NEL over HTTPS. Over plain http the header is sent and ignored, so local development stays quiet on its own.
  • The policy needs Reporting-Endpoints on the same responses. report_to names the default group that the other middleware declares, so registering this one on its own — or excluding AddReportingEndpointsHeader from a route that keeps 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.01.0 — means no header at all rather than a silently corrected one. Keeping the policy off specific routes works the same way as for the endpoints header: register on a group, then exclude the 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.