I'm encountering an issue where Laravel events are firing multiple times, but only in the production environment.
Environment:
We are dispatching Laravel events like this:
event(new ApplicationStatusChanged($application));
In production, these events trigger multiple times for a single operation. For example, a single POST request causes the event listener to run 2 times.
Check on direct GET request for Test and getting the same.
This does not happen in the local development environment.
We're trying to understand:
We've ruled out:
After hours of debugging, I finally figured out the root cause of the issue where Laravel events were firing multiple times, but only on the server (production), not locally.
Laravel automatically discovers and registers event listeners if they are placed in the App\Listeners
directory and follow a certain structure.
Specifically, Laravel will auto-register any method named
handle
or__invoke
in the listener, and bind it to the type-hinted event in its method signature.
In my case:
The listener class had a __construct()
method defined.
Because of that, Laravel didn’t auto-discover the listener locally (as expected), but the server environment did, likely due to differences in how class discovery or optimization was handled in production.
On top of that, I had also manually registered the listener in EventServiceProvider
, thinking auto-discovery wasn’t happening.
So, in production, the listener was getting registered twice:
Once via manual registration
Once via auto-discovery
This caused the event to fire multiple times, but only on the server.
I removed the unnecessary __construct()
method from the listener, making it compatible with Laravel's auto-discovery mechanism.
Then, I removed the manual listener registration from the EventServiceProvider
.
After making these changes, both local and production environments behaved consistently, and the events were no longer fired multiple times.
Avoid adding constructors to listener classes unless absolutely necessary.
Don’t register listeners manually if you’re placing them in the default App\Listeners
directory and following Laravel conventions.
Always check for duplicate listener registrations when you see events firing more than expected.