Laravel PHP version
Laravel PHP version requirements, and switching PHP safely
Laravel 13 runs on PHP 8.3 to 8.5 and Laravel 12 on 8.2 to 8.5, so both can move to a PHP release fixed for PHP2Shell. Laravel 10 and 11 are past their security end dates. After the switch, restart queue workers and other long-running processes, which keep running the PHP build they started with.
Last reviewed against Laravel's release notes and support policy, the Composer documentation and php.net.
Requirements table
PHP versions supported by each Laravel release
Laravel ships one major release a year and supports each with 18 months of bug fixes and two years of security fixes. The PHP range belongs to the major version your application is built on.
| Release | PHP | Released | Bug fixes until | Security fixes until | Status on 27 October 2026 | PHP2Shell-fixed releases it can run |
|---|---|---|---|---|---|---|
| Laravel 13 | 8.3 to 8.5 | 17 March 2026 | Q3 2027 | 17 March 2028 | Bug and security fixes (13.38.0, 20 October 2026) | 8.3.36, 8.4.27 or 8.5.12 |
| Laravel 12 | 8.2 to 8.5 | 24 February 2025 | 13 August 2026 | 24 February 2027 | Security fixes only (12.70.1, 21 October 2026) | 8.2.35, 8.3.36, 8.4.27 or 8.5.12 |
| Laravel 11 | 8.2 to 8.4 | 12 March 2024 | 3 September 2025 | 12 March 2026 | End of life | 8.2.35, 8.3.36 or 8.4.27 |
| Laravel 10 | 8.1 to 8.3 | 14 February 2023 | 6 August 2024 | 4 February 2025 | End of life | 8.2.35 or 8.3.36 |
An application still on PHP 8.1 is on Laravel 10 or older, the last release that accepted 8.1, and 8.1 has no PHP2Shell release from the PHP project. The PHP branch dates are on PHP end of life dates, and other frameworks and applications are on the PHP version requirements hub.
Sources: Laravel release notes and support policy; Laravel framework releases
composer.json
Where a Laravel app declares its PHP version
Two entries in composer.json decide
which PHP your application will accept and which PHP Composer
plans for. They do different jobs, and mixing them up is how a
build that passed on a laptop fails on a server.
{
"require": {
"php": "^8.3",
"laravel/framework": "^13.0"
},
"config": {
"platform": {
"php": "8.3.36"
}
}
} require.php
is the range the application supports. A fresh Laravel 13
application ships with ^8.3, which
Composer reads as 8.3 or later but below 9.0. By default,
Composer also writes a PHP version check into the autoloader it
generates, based on the requirements of the packages it
installed.
config.platform.php
makes Composer resolve packages as if that exact PHP were
installed. Set it to the lowest PHP your servers run and move
it in the same commit as the server change.
Two commands either side of the change
# Before: which locked packages would refuse PHP 8.5?
composer prohibits php 8.5
# After, on each server: real PHP and extensions against what is installed
composer check-platform-reqs --no-dev prohibits (also called
why-not) names every locked
package whose constraints rule out the PHP version you give it.
check-platform-reqs ignores the
platform pin and compares the server's real PHP and extensions
with the packages installed there. Composer itself recommends
running it as part of deployment whenever a platform pin is set.
Commit composer.lock so every
server installs the set of packages you tested.
Sources: Composer basic usage; Composer config; Composer commands; Composer version constraints
Every PHP process
Four places PHP runs in a Laravel deployment
Many Laravel applications run more than one kind of PHP process. Each of these has to end up on the fixed build, and each gets there differently.
Web requests through PHP-FPM
Laravel's sample nginx configuration hands requests to a socket
named after the PHP branch, such as
php8.3-fpm.sock. A patch within
the branch keeps the socket and needs only an FPM restart. A
branch change means pointing nginx at the new branch's socket.
Queue workers
Laravel describes queue:work
processes as long-lived: they hold the booted application in
memory and do not see changes until restarted. A worker started
last week is still last week's PHP process, whatever has been
installed since.
Horizon, Octane and Reverb
Horizon runs its own pool of queue workers, and Octane and Reverb
are long-running servers in their own right. Laravel's
deployment guide lists Octane and Reverb among the services to
reload after a deployment, and Horizon's guide asks for
horizon:terminate.
Artisan, Composer and deploy scripts
These use the command-line PHP, which can be a different build
from the one PHP-FPM runs. A bare php
in a script or Supervisor entry means whichever binary is first
on the path, so a server with two branches installed can run
the old one without anyone noticing.
Sources: Laravel deployment; Laravel queues; Laravel Horizon
Long-running processes
Getting workers onto the new PHP
Laravel's restart commands ask each worker to finish its current job and exit. Your process monitor then starts a replacement, and the replacement is what loads the new PHP. Without a monitor, the workers simply stop.
Restart the workers
# Laravel 12 and 13: restart queue workers, Reverb and Octane in one go
php artisan reload
# The individual commands
php artisan queue:restart
php artisan horizon:terminate queue:restart signals through
your cache store, and workers check for it between jobs, so a
worker busy with a long job finishes it first. That is the
intended behaviour; it is also why the restart is not instant.
When the branch changes, edit the monitor too
Laravel's Supervisor example starts workers with a bare
php. If you are moving branch,
name the new binary explicitly. On Debian, each branch installs
its own, such as /usr/bin/php8.4.
[program:laravel-worker]
command=/usr/bin/php8.4 /var/www/app/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
Supervisor's restart does not
re-read configuration files, so after editing the entry apply it
with reread and
update:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status Sources: Laravel: reloading services; Laravel: queue workers and deployment; Supervisor: running supervisorctl
Upgrade steps
Moving a Laravel application to a fixed PHP release
Installing PHP itself is covered in how to update PHP and, for Ubuntu, Debian and the RHEL family, the Linux guide. The steps below are the Laravel side of the change.
- Confirm the framework version.
php artisan aboutlists the Laravel version and the command-line PHP version together. Read the PHP range for that release from the table. - Choose the target. Within the same branch, take its fixed release. If you are changing branch, pick one inside your Laravel release's range, and prefer 8.3 or later so PHP 8.2's end of life on 31 December 2026 does not force a second move.
- Test before you touch production.
Raise
config.platform.php, runcomposer prohibits phpwith the new version, update what it names, and run your test suite on the target PHP. - Install the fixed PHP on every server. Web servers, queue and Horizon hosts, and any machine that runs scheduled Artisan commands all need it, including ones behind a load balancer that are easy to forget.
- Repoint anything that names a branch.
For a branch change, update the nginx socket path and the binary
in Supervisor entries, then run
supervisorctl rereadandupdate. - Restart, in order. Restart
PHP-FPM, then run
php artisan reload(orqueue:restartandhorizon:terminate) so every long-running process is replaced. - Verify. Work through the checks below on each server.
Confirm it worked
Proving every Laravel process moved
- The command line reports the fixed release.
The Environment section's PHP Version line should read 8.3.36, 8.4.27, 8.5.12 or later on its branch, or a distribution package your vendor lists as fixed.php artisan about --only=environment - Composer agrees.
composer check-platform-reqs --no-devpasses on each server. - The web tier is on the same build. The FPM service you restarted is the one nginx points at. On RHEL-family servers,
dnf needs-restartinglists processes that still need a restart after the update. - Workers came back.
sudo supervisorctl statusshows each worker program running again after the restart, and jobs are being processed. - The exposure is reviewed. If the application was reachable on a vulnerable build, work through the compromise checks; if a server cannot be patched soon, read whether to take it offline.
Hosting a PHP application you did not build? The Nextcloud and Moodle pages cover their own PHP rules, including their command-line tools.
Laravel questions
Laravel PHP version FAQs
What PHP version does Laravel 12 need?
PHP 8.2 to 8.5. Laravel 12's bug fixes ended on 13 August 2026 and its security fixes run until 24 February 2027. Any of 8.2.35, 8.3.36, 8.4.27 or 8.5.12 closes PHP2Shell; 8.3 or later avoids PHP 8.2's end of life on 31 December 2026 and is also what Laravel 13 needs.
Can Laravel 10 run on PHP 8.4?
Not as a supported pairing. Laravel lists PHP 8.1 to 8.3 for Laravel 10, so 8.3.36 is the newest PHP2Shell release it supports. Laravel 10's security fixes ended on 4 February 2025, so the framework upgrade, to 12 or 13, should follow the PHP patch quickly.
What does config.platform.php do in composer.json?
It tells Composer to resolve dependencies as if the given PHP version were installed, whatever PHP the machine running Composer has. That keeps a developer laptop on PHP 8.5 from locking packages a PHP 8.3 server cannot run. Composer warns that it also stops the real PHP being checked, so run composer check-platform-reqs on the server as part of each deployment.
Do I need to restart queue workers after updating PHP?
Yes. A queue worker is one long-running PHP process, and Laravel's documentation says workers do not pick up changes until they restart. Run php artisan reload, or php artisan queue:restart and php artisan horizon:terminate, and let your process monitor start fresh workers on the new build.
Will updating Laravel fix PHP2Shell?
No. Laravel is a Composer package that runs on PHP; updating it cannot replace the PHP runtime. Laravel 13.38.0 and 12.70.1 are current framework releases, but the PHP2Shell fix only arrives with a patched PHP build on the server.
Know your exposure
Find every PHP server you run
Patching the server you know about is the easy part. A vulnerability scan finds the forgotten staging site, the old microsite and the appliance with PHP inside, then tells you which ones are still exposed.