PHP 8.3 Migration for WordPress: A Checklist That Worked on 47 Sites

PHP 8.3 is the deadline most WordPress hosts moved to in 2025. Our migration of 47 client sites broke things on six of them — about one in eight. Here is the checklist that handled the other 41 cleanly, and the patterns of the six that did not.

The pre-migration checklist

  1. WordPress core on the latest minor. 6.5+ is fine with 8.3. Older cores have unresolved deprecation warnings that are now fatal.
  2. All active plugins on their latest version. Plugin compatibility regressions are 80 percent of the breakage we saw.
  3. Theme PHP files audited for deprecated functions. create_function(), the old each() pattern, dynamic class properties without declaration.
  4. Test a staging clone first. Always. We never migrated production without a staging pass.

What actually broke

Across the six sites that hit issues, the patterns were:

  • Dynamic properties on undeclared classes. 8.2 deprecated this; 8.3 makes it a fatal in some cases. Almost always in an abandoned plugin we then replaced.
  • Calling strtolower() on null. Strict type changes mean what used to be a soft warning is now a fatal. Audit any code that handles user input or plugin metadata.
  • Removed utf8_encode and utf8_decode. These were deprecated then removed. Old import plugins were the worst offenders.
  • SOAP and ext-imap quirks. Two sites used ancient SOAP integrations that needed a workaround.

The migration step-by-step

  1. Clone production to staging.
  2. Switch staging to PHP 8.3 in your host’s control panel or via WP-CLI’s host-specific command.
  3. Visit every public template and the entire admin. Note any white screen or error.
  4. Tail wp-content/debug.log for one hour of normal use. Almost everything that will fail in production fails here first.
  5. Run WooCommerce checkout end-to-end on test cards if it is a store.
  6. Cut over production. Keep the 8.2 environment available for 48 hours as a rollback.

The single most useful thing

Enable WP_DEBUG_LOG with display_errors = Off on staging for the migration window. Every deprecation that will bite you logs there first. It is the cheapest insurance you can buy.

8.3 is faster and the migration is usually clean. The pattern across the sites that broke was always “old plugin nobody touches” — the migration was a useful prompt to retire those.

WordPress 6.8 Performance Wins: What Actually Made My Sites Faster

WordPress 6.8 shipped a handful of real performance improvements that mostly happened under the hood. Not all of them moved the needle on production sites. Here is what actually did, measured across 47 sites we manage, with the boring honest result for each.

The wins that showed up in the data

  • Improved wp_query caching for archive pages. Across our sample, archive TTFB dropped 12 to 28 percent without any code changes. Bigger wins on sites with deep taxonomy queries.
  • Reduced autoloaded options on fresh installs. Saves a few hundred KB of wp_options reads per request. Modest, but cumulative on shared hosting.
  • Faster admin asset enqueueing. Editor screens loaded 200-400ms faster on average. Real if you spend time in wp-admin.

The wins that did not

  • “Improved emoji handling.” Saved 20ms on requests that did not have emoji. Did nothing measurable on requests that did.
  • Block editor “fast preview” mode. Editor felt faster subjectively. Lighthouse scores on the public site unchanged.

What we changed in response

Three concrete actions after rolling out 6.8 across the fleet:

  1. Stopped manually caching archive queries in two custom plugins. The core cache now does it correctly. Removed about 400 lines of code.
  2. Audited autoloaded options on every site. The 6.8 changes made the threshold for “this matters” lower, not higher. We trimmed 60 to 200 autoloaded entries per site on average.
  3. Re-baselined Core Web Vitals. LCP and INP both improved slightly on most sites with no other changes. Worth refreshing your baseline so you do not chase ghosts in subsequent optimisation work.

What to do this week

Update if you have not. Then run wp transient delete --all and wp cache flush to make sure the new cache logic is starting from a clean slate. Re-measure Core Web Vitals 48 hours after. Most sites get a free 5-15 percent on LCP from the archive-cache improvements alone.

6.8 is a quiet release, not a flashy one. That is usually the kind that pays off in maintenance budget over the next year.

The 7 WordPress Plugins We Refuse to Install (And What We Use Instead)

Twenty years of WordPress and roughly a thousand sites in. There are plugins we will not install. Each has a clean replacement we use instead. This list is not “these are bad plugins” — it is “this is the tradeoff we made and why”.

1. Heavy “all-in-one SEO” plugins on small sites

If the site does not need every SEO feature, Yoast or RankMath is overhead. What we use: SEOPress Free or Slim SEO for sites where 90 percent of the SEO settings will never be touched.

2. Slider Revolution

Powerful, heavy, security history. What we use: a Swiper.js implementation in 200 lines of theme code, or Elementor’s slider if Elementor is already loaded.

3. “Cleanup” plugins that delete revisions and transients

These run on a cron and frequently delete things you wanted. What we use: a small mu-plugin that prunes old revisions on a schedule with a configurable retention window.

4. WP Statistics / WP Slimstat

Self-hosted analytics that bloats the database fast. What we use: Plausible or Fathom on a separate domain.

5. Generic anti-spam plugins on production sites

Akismet alone misses realistic AI-written spam; honeypot-only plugins miss the headless cases; CAPTCHA-only plugins hurt conversions. What we use: QWeb Spam Shield. Reads the submission with AI, layers honeypot + timing underneath, and includes a Mail Guard for outbound reputation protection. It is the one we install on every production site we touch.

6. Page-builder add-on bundles

“Ultimate addons for X” packages that ship 40 widgets we use 3 of. What we use: the specific widget plugin from the original author, or a custom block.

7. WP File Manager / “FTP in admin” plugins

Convenience that is also an attack surface. What we use: SSH for staff, SFTP for clients who need it. Never an admin-side file manager.

The pattern

Every plugin on this list shares a property: it does more than it needs to, and the “more” is what causes the problems. Smaller, more focused plugins age better. We never regret installing fewer plugins, and we usually regret the all-in-one bundle.

None of these are bad plugins. They are just not the right fit for the way we work. Your list will be different. The exercise of writing yours is worth an afternoon.

Image Optimization on WordPress: The WebP and AVIF Workflow That Stuck

Three years of WebP, two of AVIF, dozens of tested plugins. The image-optimisation workflow that stuck across our entire client portfolio is short, boring, and effective. Here is what it is, and what stopped working that we used to recommend.

The workflow

  1. Resize at upload to a maximum of 2400px on the longest edge before any further processing. Most uploads are 5000+ from phones and the extra resolution does nothing.
  2. Compress to WebP automatically with a server-side conversion. Quality 80 is the sweet spot. Below 70 is visibly worse on photos. Above 85 is wasted bytes.
  3. Generate AVIF for the hero image only. AVIF compresses better than WebP but encoding is expensive. Doing it for every image bloats your media library; doing it for the LCP image specifically pays off.
  4. Serve responsive variants via WordPress’s built-in srcset. Do not override it with a CDN URL trick that loses the responsive behaviour.
  5. Lazy-load below-the-fold images with loading="lazy". Skip the above-the-fold image — lazy-loading the LCP hero hurts your Core Web Vitals.

What stopped working

  • JPEGtran-style optimisers as a standalone strategy. They squeeze 10-15 percent out of a JPEG; switching to WebP saves 30-40 percent. The compression is no longer where the win is.
  • CDN-based automatic conversion with no local fallback. Worked great until the CDN had an incident and every image on every site went 404 for 90 minutes. We now keep the original on origin.
  • Aggressive lazy-loading with native attribute alone. Browser support is great, but native loading="lazy" can fire late and cause LCP regressions on slow connections. We now use it everywhere except the hero.

The stack

We use ShortPixel as the conversion service (any of the modern image-conversion plugins work equivalently — ShortPixel, Imagify, EWWW). Configuration: WebP for all, AVIF for the hero only, originals preserved. CDN at the edge with WebP/AVIF content negotiation. WordPress core handles the srcset.

The measurement

Median image weight on the homepage of a typical client site, before and after:

  • JPEG, no compression: 1,840 KB
  • JPEG, optimised: 1,520 KB
  • WebP @ 80: 740 KB
  • AVIF @ 80 (hero only): 510 KB

The format change is roughly 2x the savings of compression alone. Combine both and Core Web Vitals usually go from “Needs Improvement” to “Good” on the image score, no other changes.

Boring, working, stable. That is what we wanted three years ago and that is what we have now.

WordPress Multisite vs Separate Installs: When Each Wins

WordPress Multisite is one of those features people either love or never touch. It saves time on the right job and creates pain on the wrong one. After running both models for client portfolios, here is when each actually wins.

When multisite wins

  • Many similar sites with shared management. A university with 80 department sites. A franchise with 200 location pages. A media group with 12 brand sites that all share the same plugin stack. Updating once and propagating is the whole point.
  • Centralised user management. One user account with different roles on different sites is genuinely useful when content teams overlap.
  • Shared media library when the assets logically belong to one organisation.
  • Tightly controlled plugin set. The super-admin enforces what is allowed; site admins cannot install rogue plugins. This is a feature for IT teams.

When separate installs win

  • Sites with different stacks. One needs WooCommerce, another needs LearnDash, another is a simple brochure. Multisite forces you to share more than you want.
  • Sites that need different update cadences. Some clients want every WordPress update yesterday; others wait six months. Multisite’s “update everything together” is hostile to that.
  • Sites with very different traffic profiles. One viral site can bring down the entire multisite network. Separation isolates the blast radius.
  • Sites you might sell or hand off. Migrating one site out of a multisite is painful. Separate installs are portable.

The honest gotchas of multisite

  • Plugin compatibility is worse. Some plugins simply do not work on multisite. The list shrinks every year but still exists.
  • Hosting choice narrows. Many cheaper managed hosts do not support multisite or charge a premium for it.
  • Backups are all-or-nothing for the network. Per-site restore is rarely as clean as you want.
  • Subdomain vs subdirectory is a one-way decision. Pick wrong and migration is a project.

The honest gotcha of separate installs

Tooling burden. Running 30 separate WordPress installs requires the kind of automation we covered in our 50-sites article. Without it, the “freedom” of separate installs becomes “30 things to remember to update”.

The shorthand

If the sites should be managed together because they share an organisation, a stack, and a release cycle: multisite. If the sites only happen to be managed by the same agency but are otherwise independent: separate installs. The wrong choice is fixable but expensive — make it deliberately on the first build.