PHPackages                             moneo/laravel-request-forwarder - 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. [Queues &amp; Workers](/categories/queues)
4. /
5. moneo/laravel-request-forwarder

ActiveLibrary[Queues &amp; Workers](/categories/queues)

moneo/laravel-request-forwarder
===============================

Laravel Request Forwarder allows you to forward incoming requests to another addresses.

v2.0.1(2mo ago)784144MITPHPPHP ^8.2CI passing

Since Mar 6Pushed 2mo ago3 watchersCompare

[ Source](https://github.com/moneo/laravel-request-forwarder)[ Packagist](https://packagist.org/packages/moneo/laravel-request-forwarder)[ Docs](https://github.com/moneo/laravel-request-forwarder)[ GitHub Sponsors](https://github.com/moneo)[ RSS](/packages/moneo-laravel-request-forwarder/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (6)Dependencies (16)Versions (12)Used By (0)

[![](https://camo.githubusercontent.com/a0db3beb6cea22a573f4bc0c2c929a820dc278df9d67cb890ff9df766e8c5126/68747470733a2f2f62616e6e6572732e6265796f6e64636f2e64652f4c61726176656c25323052657175657374253230466f727761726465722e706e673f7468656d653d6c69676874267061636b6167654d616e616765723d636f6d706f7365722b72657175697265267061636b6167654e616d653d6d6f6e656f2532466c61726176656c2d726571756573742d666f72776172646572267061747465726e3d617263686974656374267374796c653d7374796c655f31266465736372697074696f6e3d466f72776172642b696e636f6d696e672b72657175657374732b746f2b616e6f746865722b616464726573736573266d643d312673686f7757617465726d61726b3d3026666f6e7453697a653d313030707826696d616765733d736572766572)](https://camo.githubusercontent.com/a0db3beb6cea22a573f4bc0c2c929a820dc278df9d67cb890ff9df766e8c5126/68747470733a2f2f62616e6e6572732e6265796f6e64636f2e64652f4c61726176656c25323052657175657374253230466f727761726465722e706e673f7468656d653d6c69676874267061636b6167654d616e616765723d636f6d706f7365722b72657175697265267061636b6167654e616d653d6d6f6e656f2532466c61726176656c2d726571756573742d666f72776172646572267061747465726e3d617263686974656374267374796c653d7374796c655f31266465736372697074696f6e3d466f72776172642b696e636f6d696e672b72657175657374732b746f2b616e6f746865722b616464726573736573266d643d312673686f7757617465726d61726b3d3026666f6e7453697a653d313030707826696d616765733d736572766572)

Laravel Request Forwarder
=========================

[](#laravel-request-forwarder)

[![Latest Version on Packagist](https://camo.githubusercontent.com/ef536010ec6b260e203964198ea8bc5784fd3d039ebc27ed678969bad8af7c1a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d6f6e656f2f6c61726176656c2d726571756573742d666f727761726465722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/moneo/laravel-request-forwarder)[![GitHub Tests Action Status](https://camo.githubusercontent.com/82cfe4ec886a4d8d7266302cfb95a50c34441d88e7837fcdf0036dde42aedc75/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6d6f6e656f2f6c61726176656c2d726571756573742d666f727761726465722f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/moneo/laravel-request-forwarder/actions?query=workflow%3Arun-tests+branch%3Amain)[![PHPStan](https://camo.githubusercontent.com/230ee1fce2f8a575f5fb1fe77161d50876e76665d168fbaaaa0a2c43f280f214/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6d6f6e656f2f6c61726176656c2d726571756573742d666f727761726465722f7068707374616e2e796d6c3f6272616e63683d6d61696e266c6162656c3d7068707374616e267374796c653d666c61742d737175617265)](https://github.com/moneo/laravel-request-forwarder/actions?query=workflow%3APHPStan+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/ab60df66c1f67482dc3e28bf5820a22e20b41711f39f9a972660bd9ab5b382be/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6d6f6e656f2f6c61726176656c2d726571756573742d666f727761726465722f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/moneo/laravel-request-forwarder/actions?query=workflow%3A%22Fix+PHP+code+style+issues%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/8d581aae4a3eb06fece3a3aab8d3320ee6629aec2ccc7005473ab8bf85d68434/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6d6f6e656f2f6c61726176656c2d726571756573742d666f727761726465722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/moneo/laravel-request-forwarder)

**Forward incoming HTTP requests to multiple destinations -- asynchronously, reliably, and with zero config overhead.**

Some webhook providers only allow a single callback URL. This package sits behind that URL and fans the request out to as many targets as you need -- different servers, Slack, Discord, or any custom destination -- all processed through Laravel's queue system with automatic retries and failure logging.

### Highlights

[](#highlights)

- **Async by default** -- Requests are dispatched to a queue job so your response time stays fast.
- **Multi-target fan-out** -- Forward a single incoming request to one or many endpoints in parallel.
- **Custom providers** -- Ship your own delivery logic by implementing a single interface. Discord provider included.
- **Events on every delivery** -- `WebhookSent` and `WebhookFailed` events let you build dashboards, alerts, or audit logs.
- **Automatic retries** -- Configurable `tries` and exponential `backoff` per queue job, with failure logging out of the box.

[![How it works](https://raw.githubusercontent.com/moneo/laravel-request-forwarder/main/workflow.png)](https://raw.githubusercontent.com/moneo/laravel-request-forwarder/main/workflow.png)

---

Table of Contents
-----------------

[](#table-of-contents)

- [Requirements](#requirements)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Configuration Reference](#configuration-reference)
- [Usage](#usage)
    - [Middleware](#middleware)
    - [Facade / Programmatic](#facade--programmatic)
    - [Multiple Webhook Groups](#multiple-webhook-groups)
    - [Per-Target Headers](#per-target-headers)
    - [Per-Target Timeout](#per-target-timeout)
    - [Custom Providers](#custom-providers)
- [Events](#events)
- [Queue &amp; Retry](#queue--retry)
- [Error Handling &amp; Logging](#error-handling--logging)
- [Upgrade Guide (v1.x to v2.0)](#upgrade-guide-v1x-to-v20)
- [Testing](#testing)
- [Changelog](#changelog)
- [Contributing](#contributing)
- [Maintainers](#maintainers)
- [Security Vulnerabilities](#security-vulnerabilities)
- [Credits](#credits)
- [License](#license)

---

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

[](#requirements)

- **PHP** 8.2 or higher
- **Laravel** 11.x or 12.x

---

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

[](#installation)

Install the package via Composer:

```
composer require moneo/laravel-request-forwarder
```

Publish the configuration file:

```
php artisan vendor:publish --tag="request-forwarder-config"
```

---

Quick Start
-----------

[](#quick-start)

**1.** Set your target URL in `.env`:

```
REQUEST_FORWARDER_DEFAULT_URL=https://your-target.com/webhook

```

**2.** Add the middleware to any route you want to forward:

```
Route::middleware('request-forwarder')
    ->post('/webhook', fn () => response()->json(['status' => 'ok']));
```

That's it. Every request hitting `/webhook` is now forwarded to your target asynchronously via the queue.

---

Configuration Reference
-----------------------

[](#configuration-reference)

After publishing, the config lives at `config/request-forwarder.php`:

```
return [
    'default_webhook_group_name' => 'default',

    'webhooks' => [
        'default' => [
            'targets' => [
                [
                    'url'    => env('REQUEST_FORWARDER_DEFAULT_URL', 'https://example.com/webhook'),
                    'method' => 'POST',
                ],
            ],
        ],
    ],

    'timeout'    => 30,

    'queue_name'  => env('REQUEST_FORWARDER_QUEUE', ''),
    'queue_class' => Moneo\RequestForwarder\ProcessRequestForwarder::class,

    'tries'   => 3,
    'backoff' => [5, 30, 60],

    'log_failures' => true,
];
```

**Key-by-key breakdown:**

- **`default_webhook_group_name`** -- Which webhook group to use when the middleware is called without a parameter.
- **`webhooks`** -- A map of named groups. Each group contains a `targets` array. Every target needs at least a `url`. Optional keys per target: `method` (default `POST`), `provider`, `headers`, `timeout`.
- **`timeout`** -- Global HTTP timeout in seconds for outgoing requests. Can be overridden per target.
- **`queue_name`** -- The queue connection/name for async jobs. Leave empty to use Laravel's default queue.
- **`queue_class`** -- The job class used for dispatching. Override this if you need custom job logic.
- **`tries`** -- How many times a failed job is retried before being marked as permanently failed.
- **`backoff`** -- Seconds to wait between retries. Accepts a single integer or an array for progressive backoff.
- **`log_failures`** -- When `true`, failed deliveries are written to your Laravel log.

---

Usage
-----

[](#usage)

### Middleware

[](#middleware)

Attach the `request-forwarder` middleware to any route. The middleware dispatches a queue job and lets the request continue normally -- your users see no delay.

```
// Forward using the default webhook group
Route::middleware('request-forwarder')
    ->post('/webhook', fn () => 'OK');

// Forward using a named webhook group
Route::middleware('request-forwarder:payments')
    ->post('/payments/webhook', fn () => 'OK');
```

### Facade / Programmatic

[](#facade--programmatic)

Use the `RequestForwarder` facade when you need more control:

```
use Moneo\RequestForwarder\Facades\RequestForwarder;

// Dispatch to queue (async)
RequestForwarder::sendAsync($request);
RequestForwarder::sendAsync($request, 'payments');

// Trigger immediately (sync) -- useful in jobs or artisan commands
RequestForwarder::triggerHooks('https://original-url.com/hook', ['key' => 'value']);
RequestForwarder::triggerHooks('https://original-url.com/hook', $data, 'payments');
```

### Multiple Webhook Groups

[](#multiple-webhook-groups)

Define as many groups as you need. Each group is independently configurable:

```
'webhooks' => [
    'default' => [
        'targets' => [
            ['url' => 'https://primary-backend.com/hook', 'method' => 'POST'],
        ],
    ],
    'payments' => [
        'targets' => [
            ['url' => 'https://accounting.internal/stripe', 'method' => 'POST'],
            [
                'url'      => 'https://discord.com/api/webhooks/...',
                'method'   => 'POST',
                'provider' => \Moneo\RequestForwarder\Providers\Discord::class,
            ],
        ],
    ],
],
```

### Per-Target Headers

[](#per-target-headers)

Add authentication or custom headers to individual targets:

```
'targets' => [
    [
        'url'     => 'https://api.partner.com/webhook',
        'method'  => 'POST',
        'headers' => [
            'Authorization'   => 'Bearer your-api-token',
            'X-Webhook-Secret' => 'shared-secret',
        ],
    ],
],
```

### Per-Target Timeout

[](#per-target-timeout)

Override the global timeout for slow endpoints:

```
'targets' => [
    [
        'url'     => 'https://slow-service.example.com/hook',
        'method'  => 'POST',
        'timeout' => 60,
    ],
],
```

### Custom Providers

[](#custom-providers)

The default provider sends JSON over HTTP. Need a different format? Implement `ProviderInterface`:

```
use Illuminate\Http\Client\Factory;
use Illuminate\Http\Client\Response;
use Moneo\RequestForwarder\Providers\ProviderInterface;

class SlackProvider implements ProviderInterface
{
    public function __construct(private readonly Factory $client)
    {
    }

    public function send(string $url, array $params, array $webhook): Response
    {
        return $this->client
            ->timeout($webhook['timeout'] ?? 30)
            ->send('POST', $webhook['url'], [
                'json' => [
                    'text' => "Webhook from {$url}\n```" . json_encode($params, JSON_PRETTY_PRINT) . '```',
                ],
            ]);
    }
}
```

Register your provider in the target config:

```
'targets' => [
    [
        'url'      => 'https://hooks.slack.com/services/T.../B.../xxx',
        'method'   => 'POST',
        'provider' => App\Webhooks\SlackProvider::class,
    ],
],
```

The package validates that every provider class exists and implements `ProviderInterface` before instantiation. Providers are resolved through Laravel's container, so constructor injection works out of the box.

---

Events
------

[](#events)

Every delivery attempt dispatches an event you can hook into:

**`WebhookSent`** -- Dispatched after a successful HTTP response (any status code).

- `string $sourceUrl` -- The original incoming request URL.
- `string $targetUrl` -- The target the request was forwarded to.
- `int $statusCode` -- The HTTP status code returned by the target.

**`WebhookFailed`** -- Dispatched when the delivery throws any exception.

- `string $sourceUrl` -- The original incoming request URL.
- `string $targetUrl` -- The target that failed.
- `\Throwable $exception` -- The exception that was caught.

**Example listener:**

```
use Moneo\RequestForwarder\Events\WebhookSent;
use Moneo\RequestForwarder\Events\WebhookFailed;

// In a service provider or event subscriber
Event::listen(WebhookSent::class, function (WebhookSent $event) {
    logger()->info("Forwarded to {$event->targetUrl}", [
        'status' => $event->statusCode,
    ]);
});

Event::listen(WebhookFailed::class, function (WebhookFailed $event) {
    logger()->error("Forward to {$event->targetUrl} failed", [
        'error' => $event->exception->getMessage(),
    ]);
});
```

---

Queue &amp; Retry
-----------------

[](#queue--retry)

The middleware dispatches a `ProcessRequestForwarder` job. Configure its behavior in the config:

```
'queue_name' => env('REQUEST_FORWARDER_QUEUE', 'webhooks'),
'tries'      => 3,
'backoff'    => [5, 30, 60], // wait 5s, then 30s, then 60s
```

- If `queue_name` is empty, Laravel's default queue is used.
- `tries` and `backoff` are validated at runtime; invalid values fall back to safe defaults (`3` tries, `[5, 30, 60]` backoff).

**Custom job class:** If you need to customize serialization, middleware, or tagging, extend the default job and point the config to your class:

```
// app/Jobs/CustomForwarder.php
class CustomForwarder extends \Moneo\RequestForwarder\ProcessRequestForwarder
{
    public $timeout = 120;

    public function tags(): array
    {
        return ['webhook-forwarder', "group:{$this->webhookName}"];
    }
}
```

```
// config/request-forwarder.php
'queue_class' => App\Jobs\CustomForwarder::class,
```

---

Error Handling &amp; Logging
----------------------------

[](#error-handling--logging)

The package is designed to never break your application flow:

- **Inside `triggerHooks`:** Each target is processed independently. If one target fails, the remaining targets still execute. Failed targets dispatch a `WebhookFailed` event and (when `log_failures` is `true`) write an error to your Laravel log.
- **Queue job failures:** When all retries are exhausted, the job's `failed()` method logs the final error with the source URL and webhook group name.
- **Strict validation:** Invalid URLs, unsupported HTTP methods, non-positive timeouts, non-existent provider classes, and malformed config shapes all throw `InvalidArgumentException` eagerly -- so misconfigurations surface during development, not in production.

---

Upgrade Guide (v1.x to v2.0)
----------------------------

[](#upgrade-guide-v1x-to-v20)

### Runtime requirements

[](#runtime-requirements)

- PHP `8.2+` (was `8.1+`).
- Laravel `11.x` or `12.x` (Laravel 10 support dropped; it reached EOL in February 2025).

### Config validation is now strict

[](#config-validation-is-now-strict)

v2.0 fails fast on invalid configuration instead of silently ignoring it. Check your webhook groups:

- `targets` must be a non-empty array.
- Each target must have a valid `url` (passes `FILTER_VALIDATE_URL`).
- `method` must be one of: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`.
- `timeout` must be a positive number.
- `headers` must be an array of string keys with scalar values.

### Provider validation

[](#provider-validation)

- Custom providers must exist as classes and implement `ProviderInterface`.
- Invalid providers now trigger `WebhookFailed` instead of being silently skipped.

### Queue behavior

[](#queue-behavior)

- Empty `queue_name` no longer forces an empty string -- it falls back to Laravel's default queue.
- `tries` and `backoff` are validated and normalized at runtime.

### Event typing

[](#event-typing)

- `WebhookFailed::$exception` is now `\Throwable` (was `\Exception`).

### Recommended steps

[](#recommended-steps)

1. Update your dependency: `composer require moneo/laravel-request-forwarder:^2.0`
2. Re-publish the config: `php artisan vendor:publish --tag="request-forwarder-config" --force`
3. Review your webhook groups against the validation rules above.
4. Run your test suite and verify webhook flows in staging.
5. Monitor logs for any `InvalidArgumentException` entries from the package.

---

Testing
-------

[](#testing)

```
# Run the test suite
composer test

# Run static analysis
composer analyse

# Fix code style
composer format
```

---

Changelog
---------

[](#changelog)

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

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

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

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

[](#security-vulnerabilities)

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

Credits
-------

[](#credits)

- [Emir Karşıyakalı](https://github.com/emir)
- [Emre Dipi](https://github.com/emredipi)
- [Mucahit Cucen](https://github.com/mcucen)
- [Semih Keskin](https://github.com/semihkeskindev)
- [Taha Caliskan](https://github.com/Tahaknd)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

50

—

FairBetter than 95% of packages

Maintenance84

Actively maintained with recent releases

Popularity29

Limited adoption so far

Community17

Small or concentrated contributor base

Maturity60

Established project with proven stability

 Bus Factor2

2 contributors hold 50%+ of commits

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.

###  Release Activity

Cadence

Every ~102 days

Recently: every ~176 days

Total

8

Last Release

83d ago

Major Versions

0.0.1 → 1.0.12024-03-07

0.0.2 → 1.0.22024-03-14

1.0.2 → v2.0.02026-02-16

PHP version history (3 changes)1.0.0PHP ^8.1

0.0.1PHP ^8.0

v2.0.0PHP ^8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/8c1579caf779030cfa5b0e740ae68a7085d5e3d4ba0de2df3388c7cb0e73f6cc?d=identicon)[emir](/maintainers/emir)

---

Top Contributors

[![emredipi](https://avatars.githubusercontent.com/u/23154364?v=4)](https://github.com/emredipi "emredipi (26 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (9 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (8 commits)")[![emir](https://avatars.githubusercontent.com/u/1097170?v=4)](https://github.com/emir "emir (8 commits)")[![semihkeskindev](https://avatars.githubusercontent.com/u/32278879?v=4)](https://github.com/semihkeskindev "semihkeskindev (7 commits)")[![Tahaknd](https://avatars.githubusercontent.com/u/70923113?v=4)](https://github.com/Tahaknd "Tahaknd (4 commits)")[![mcucen](https://avatars.githubusercontent.com/u/18099064?v=4)](https://github.com/mcucen "mcucen (2 commits)")

---

Tags

asynclaravellaravel-packagemiddlewarephpqueuerequest-forwardingwebhooklaravelmoneolaravel-request-forwarder

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/moneo-laravel-request-forwarder/health.svg)

```
[![Health](https://phpackages.com/badges/moneo-laravel-request-forwarder/health.svg)](https://phpackages.com/packages/moneo-laravel-request-forwarder)
```

###  Alternatives

[laravel/scout

Laravel Scout provides a driver based solution to searching your Eloquent models.

1.7k49.4M479](/packages/laravel-scout)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k12.1M99](/packages/laravel-pulse)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9682.1M97](/packages/roots-acorn)[harris21/laravel-fuse

Circuit breaker for Laravel queue jobs. Protect your workers from cascading failures.

3786.5k](/packages/harris21-laravel-fuse)[laragear/preload

Effortlessly make a Preload script for your Laravel application.

119363.5k](/packages/laragear-preload)[simplestats-io/laravel-client

Client for SimpleStats!

4515.5k](/packages/simplestats-io-laravel-client)

PHPackages © 2026

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