SandyWP no longer offers PHP 7.4, and running it on any public URL is unsafe. On 21 October 2026 PHP fixed two critical bugs that let an attacker run code on a server without logging in, and 7.4 is end of life, so no 7.4 release will carry the fix.
If you came here to test something on PHP 7.4, the test you need now is the move off it. This post shows how to run a copy of your site on PHP 8.3, find what breaks, and fix it before you touch the real host.
We rewrote this post on 22 October 2026. The earlier version showed how to create a PHP 7.4 sandbox, which SandyWP no longer offers.
Why SandyWP no longer offers PHP 7.4
The two bugs are CVE-2026-109618 and CVE-2026-109619. Both are rated critical, CVSS 9.2, and both allow remote code execution without a login (php.net security advisory, read 2026-10-22).
PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12 fix them. The GitHub advisories for CVE-2026-109618 and CVE-2026-109619 list every version below those as affected.
php.net says every PHP install that serves web requests is at risk, PHP-FPM included. The risk is highest on Apache with mod_php, where the PHP team demonstrated an exploit that fires before any application code runs.
On a WordPress site, "before any application code" means before WordPress loads. No security plugin gets a chance to see that request, let alone block it.
PHP 7.4 reached end of life on 28 November 2022, and its last release was 7.4.33. PHP 8.1 followed on 31 December 2025 (php.net end of life).
Because the bugs are this serious, the PHP security team also published minimal source patches for 7.4, 8.0 and 8.1, for people who compile PHP themselves. They are not releases, and the PHP project no longer supports those branches.
Every SandyWP sandbox runs PHP under Apache with mod_php, on a public URL. So on 22 October 2026 we removed PHP 7.4 and 8.1 and moved every active sandbox that used them to PHP 8.3, with files, database and URL unchanged (changelog).
SandyWP now offers PHP 8.2, 8.3, 8.4 and 8.5. The default is 8.3.
Can a site stay on PHP 7.4 a little longer?
Not on a public URL. A site on stock PHP 7.4 now has a known remote code execution bug that no official release will close.
Some Linux distributions and paid extended-support vendors ship their own 7.4 builds with backported fixes. If your host uses one, ask them in writing whether it includes the fixes for both CVEs, and treat anything short of a clear yes as a no.
Pick the version you move to with the calendar in mind. PHP 8.2 reaches end of life on 31 December 2026, while 8.3 gets security fixes until 31 December 2027 (php.net supported versions).
WordPress itself recommends 8.3 as the minimum (Make WordPress Core, 22 May 2026). If your host offers it, go straight there and treat 8.2 as a stop on the way.
How to test a PHP 7.4 site on PHP 8.3 first
Seven steps, and the live site stays untouched until the last one.
1. Clone the live site into a sandbox
Install the SandyWP Cloner plugin on the live site, so you test its real plugins, theme and data rather than a fresh install. The Cloner runs on PHP 7.4, and it only reads the site, never writes to it.
In wp-admin, open Tools → SandyWP Cloner, click Connect to SandyWP and approve the connection.
Open Trim the clone and tick Skip media library, then click Clone to SandyWP. Uploads are usually the bulk of a site, a PHP test rarely needs them, and the Free plan has 256 MB of storage.
The copy appears on the Clone page of your SandyWP dashboard, running PHP 8.3, the SandyWP default. Only the database and wp-content travel, so it also gets clean WordPress core at the current stable release.
If the live site is behind on WordPress, you are testing both upgrades at once, and wp-admin may ask to update the database the first time you log in. That only touches the copy.
A clone also carries your live API keys and scheduled jobs. Read how to clone a WordPress site without the copy touching production before you start clicking.
2. Turn on Debug mode before you click anything
Open the sandbox's page from your dashboard: click its name, or choose Site details in its menu. Pick Debug in the side menu, switch on Enable Debug mode and click Save changes.
That sets WP_DEBUG, logs errors to wp-content/debug.log and shows them on the page. It applies without a restart, and Debug mode is on every plan, Free included.
3. Click through what the site actually does
PHP only reports problems in code it loads and runs. A template nobody opens, or a settings screen nobody visits, stays silent.
So load the pages people use, not just the homepage: the dashboard, each plugin's settings screen, a form submission, a search, the checkout or member area if there is one, and every custom page template.
Scheduled tasks run code nobody clicks. With SSH access, which is on paid plans, wp cron event run --due-now runs every task that is due in one go.
4. Read the log, fatal errors first
Back on the Debug page, the Debug log panel shows the tail of debug.log, with filters for Fatal, Warning, Notice and Deprecated. Refresh it after each round of clicks.
Fatal errors are the ones to fix before the switch, because each one is a broken page or a white screen on the live site. Deprecations change nothing yet: they are the list of what will break in a later PHP version.
These are the lines you are most likely to see on a site that has only ever run 7.4:
| What the log line contains | Since | What it means |
|---|---|---|
Call to undefined function create_function() or each() |
8.0 | Code from the PHP 5 era. Update or replace that plugin. |
Array and string offset access syntax with curly braces is no longer supported |
8.0 | $str{0} where PHP now wants $str[0]. The whole file fails to load. |
Uncaught TypeError: count() |
8.0 | count() on null or anything else that is not an array or Countable. A warning on 7.4, a fatal now. |
Undefined array key |
8.0 | Was a notice on 7.4, now a warning. Noisy, usually harmless. |
Passing null to parameter |
8.1 | A built-in function was given null. Harmless for now. |
Return type of ... should either be compatible with |
8.1 | A class implements ArrayAccess, Iterator or Countable without return types. |
Creation of dynamic property |
8.2 | An object got a property its class never declared. Common in older plugins. |
One PHP 8 change writes nothing to the log at all. 0 == "abc" was true on 7.4 and has been false since 8.0, so code that relied on the old answer quietly takes the other branch (PHP 8.0 migration guide).
That is why clicking through the real flows matters more than any scanner.
5. Fix it so it runs on both versions
On an inherited site, fatal errors usually come from outdated plugins and themes, not from WordPress core. Update them on the copy and test again, and a lot of the list disappears without touching code.
Premium plugins often refuse to update outside their licensed domain. Download the current ZIP from the vendor and upload it in wp-admin instead.
If a plugin has no release that runs on PHP 8, that is your answer. Replace it before the switch, not after.
For code you maintain, write fixes that run on both 7.4 and 8.3, because the live site stays on 7.4 until you switch it. #[\ReturnTypeWillChange] on its own line above a method is the classic case: PHP 7.4 reads it as a comment, and 8.1 reads it as an attribute that silences the deprecation.
A static scanner speeds this part up for code you control. PHPCompatibilityWP is a PHP_CodeSniffer ruleset that reads code without running it; install it with Composer as its README describes, then point it at your theme or plugin:
vendor/bin/phpcs -p wp-content/themes/your-theme --standard=PHPCompatibilityWP --runtime-set testVersion 7.4-8.3
The 7.4-8.3 range reports problems for every version from 7.4 to 8.3, which is exactly the constraint while the live site is still on 7.4.
Treat a clean scan as a hint, not a result. WP Engine's PHP Compatibility Checker, a plugin with over 200,000 active installs, says on its own page that it is no longer maintained, checks compatibility only up to PHP 8.0, and never executes your code (wordpress.org, read 2026-10-22).
6. Try other versions on the same copy
Switching PHP on a ready sandbox restarts it for about 5 to 10 seconds and leaves files and database untouched. On paid plans it sits under PHP configuration on the sandbox's page: pick a PHP version and click Save changes (PHP settings).
On the Free plan the clone stays on 8.3, which is the version most sites should land on anyway.
If your host only offers 8.2 today, run the copy on 8.2 as well. That shows what the live site will do after the switch, and 8.3 is where it needs to be before 8.2's end of life on 31 December.
For code you maintain, one pass on 8.4 is worth it too. It deprecates a typed parameter with a = null default and no ?, which older plugins use a lot (PHP 8.4 deprecations).
7. Then switch the real host
Ship the fixes to the live site while it is still on 7.4, and take a fresh backup.
Before you switch, log errors without showing them: WP_DEBUG and WP_DEBUG_LOG on, WP_DEBUG_DISPLAY off. Point WP_DEBUG_LOG at a file path outside the web root, because the default wp-content/debug.log can be downloaded by anyone who knows the URL.
Then switch PHP in your host's control panel and load the same pages you tested, straight away. Turn the logging off again once the log stays quiet.
Anything that breaks now but did not break on the copy is usually host-level: an object cache, a WAF rule, or a PHP extension your host has not enabled for the new version. If the site goes white, this is the order to look in.
Do not plan to roll back to 7.4. Roll back the code instead, or move to 8.2 while you fix what 8.3 found.
If you publish a plugin that says Requires PHP: 7.4
WordPress 7.1 still supports PHP 7.4 (core handbook, read 2026-10-22). So a Requires PHP: 7.4 header is a promise to real users, and it is only honest while you keep testing it.
The easiest way to break 7.4 in a release is new syntax. A match expression, a nullsafe ?-> call or a named argument is a parse error on 7.4, so one of them breaks every 7.4 site that installs that release.
You do not need a 7.4 web server to catch those. A PHP 7.4 lint in a throwaway container, run from the folder you ship, prints only the errors:
docker run --rm -v "$PWD":/app:ro -w /app php:7.4-cli \
sh -c 'find . -name "*.php" -exec php -l {} \; | grep -v "^No syntax errors"'
For behaviour, run your test suite on 7.4 in CI. php.net names installs that serve web requests as the ones at risk, and a lint or a CI run is PHP on the command line with nothing listening.
Or raise the header. WordPress checks Requires PHP before it installs or updates a plugin, so sites on older PHP keep the version they have instead of breaking.
What plugin developers get covers the rest of the release loop.
If you must reproduce a 7.4-only bug, keep it on localhost
Sometimes a customer is still on 7.4 and the bug only happens there. Reproduce it on your own computer, bound to localhost, and delete it when you are done:
docker network create wp74
docker run -d --name wp74-db --network wp74 \
-e MARIADB_RANDOM_ROOT_PASSWORD=1 \
-e MARIADB_DATABASE=wordpress \
-e MARIADB_USER=wordpress \
-e MARIADB_PASSWORD=wordpress \
mariadb:11
docker run -d --name wp74 --network wp74 \
-p 127.0.0.1:8074:80 \
-e WORDPRESS_DB_HOST=wp74-db \
-e WORDPRESS_DB_USER=wordpress \
-e WORDPRESS_DB_PASSWORD=wordpress \
-e WORDPRESS_DB_NAME=wordpress \
wordpress:php7.4-apache
Open http://127.0.0.1:8074 and install WordPress as usual. If you see a database connection error, give MariaDB a few more seconds on its first start.
Three rules keep this offline, and all three matter:
- Bind to 127.0.0.1. A bare
-p 8074:80listens on every interface, which Docker's own docs call insecure by default, and on Linux those ports bypass ufw. - Use your own computer, not a server. A VPS or cloud machine has a public address by default.
- No tunnels. ngrok, Cloudflare Tunnel or a shared preview link turns this back into a public PHP 7.4 site.
Know what you are running, too. The official wordpress:php7.4-apache image was last pushed on 16 November 2022 and ships WordPress 6.1.1 (Docker Hub, checked 2026-10-22).
Update WordPress inside it if the bug needs a current version, since WordPress 7.1 still supports PHP 7.4. When you are done, remove the containers, their volumes and the network:
docker rm -f -v wp74 wp74-db
docker network rm wp74
What this test does not cover
A sandbox proves your code runs on PHP 8.3. It does not reproduce your host.
Object caches, edge caches, a WAF, open_basedir rules and the extensions compiled into your host's PHP all live at the host layer. So does load: a memory limit that only bites under real traffic needs staging on the real infrastructure.
Anything bound to the domain behaves differently too: licence checks, payment gateways, OAuth callbacks and email delivery. Upgrade testing walks through the same clone-and-compare loop for WordPress core and plugin updates.
The short version
Do not run PHP 7.4 on a public URL. No release will fix CVE-2026-109618 or CVE-2026-109619 on it, and the PHP team has demonstrated a working exploit against Apache with mod_php.
Clone the site, run the copy on PHP 8.3 with Debug mode on, fix what the log shows, then switch the real host. If you still need 7.4 to reproduce a bug, keep it on localhost and delete it afterwards.
