TechEarl
Topic · WordPress

WordPress

Building, hardening, and unbreaking WordPress without losing weekends to plugin updates.

135 articlesWritten by Ishan Karunaratne
WordPress development and security guides on plugin updates, malware removal, and production patterns.
More in WordPress
Hosting markup as MRR, operational time as hidden cost, per-client profit across four hosting tiers. The honest math for WordPress agency profitability.

How Hosting Choices Affect Agency Profitability

Hosting is one of the most leveraged decisions an agency makes for margin. The honest math: hosting markup as recurring revenue, operational time as hidden cost, the per-client profit comparison across the four hosting tiers.

Hosting for WordPress agency clients by traffic tier and workload type: shared, managed WordPress, managed VPS, self-managed. Agency-side implications.

A WordPress Hosting Decision Tree for Agencies

Hosting choices for WordPress agency clients are operational decisions, not pricing decisions. The decision tree by traffic tier and workload type: shared, managed WordPress, managed VPS, self-managed VPS. Plus the agency-side implications of each.

Managed WordPress vs VPS for agencies. Simplicity vs flexibility, price premium vs lower per-resource cost. The break-even math with real numbers.

Managed WordPress Hosting vs VPS for Agencies

Managed WordPress hosting buys you operational simplicity at a per-site price premium. VPS buys you flexibility and lower per-resource cost at the price of in-house sysadmin time. The honest comparison and the agency-side break-even math.

Rank Math vs Yoast SEO compared by feature, performance, schema, pricing, and migration cost. The honest agency take on which to pick for a WordPress site in 2026.

Rank Math vs Yoast SEO: Which WordPress SEO Plugin to Use in 2026

Rank Math's free tier covers roughly what Yoast Premium does, schema support is broader, and the plugin is lighter on resources. Yoast still has brand recognition and a more guided editor flow. The honest agency comparison, by feature, performance, schema, pricing, and migration cost.

Rocket.net managed WordPress reviewed for agencies. Performance, Cloudflare Enterprise inclusion, developer experience, vs Kinsta and WP Engine.

Rocket.net for WordPress Agencies: Honest Review

Rocket.net is the newer managed WordPress entrant that has gained agency mindshare for performance and Cloudflare Enterprise inclusion. The honest take on speed, pricing, the developer experience, and where Rocket.net wins or loses against the established hosts.

WP Engine managed WordPress reviewed for agencies. Performance, support, partner program, ACF acquisition implications, where it wins or loses.

WP Engine for WordPress Agencies: Honest Review

WP Engine is the most-recognized managed WordPress host and the default pick for many agencies. The honest take on performance, support, the agency partner program, the recent ACF acquisition implications, and where WP Engine wins or loses against alternatives.

Visitors see a fake Cloudflare verification on your WordPress site asking them to paste a command. That's ClickFix. Detection, removal, and persistence cleanup so it doesn't return.

The Fake Cloudflare Verification Attack on WordPress (ClickFix): What It Is and How to Remove It

Visitors to your WordPress site see a fake 'Cloudflare verification' page telling them to paste a command into Windows Run or Terminal. That's ClickFix, the social-engineering campaign that first appeared in early 2024 and exploded across compromised WordPress sites by autumn. What it does, where the injection lives in your site, and how to clean it without missing the persistence.

Kinsta managed WordPress hosting reviewed for agencies. Where value justifies price, where it does not, agency partner program, alternatives at each tier.

Kinsta for WordPress Agencies: Honest Review

Kinsta is the premium managed WordPress host most agencies eventually consider. The honest take: where the value justifies the price, where it does not, the agency partner program math, and the alternatives at each tier.

Bluehost honest review. Where it fits in 2026, the real operational ceiling, the migration path when sites outgrow it. From an agency operator's perspective.

Bluehost for WordPress: Honest Take for Small Sites

Bluehost has the WordPress.org recommendation and the lowest entry-tier price in mainstream hosting. The honest take on where Bluehost legitimately fits in 2026, the real operational ceiling, and the migration path for sites that outgrow it.

Cloudways managed VPS for WordPress agencies. The hybrid model in practice, DigitalOcean acquisition implications, where it wins or loses for agencies.

Cloudways for WordPress Agencies: Honest Review

Cloudways is the agency host that gives you VPS flexibility with managed-WordPress simplicity, at a per-site price closer to shared hosting. The honest take on what that hybrid actually means in practice, plus the DigitalOcean acquisition implications.

Honest Divi vs Elementor comparison for agencies. Workflow speed, ecosystem, hiring, client handoff, performance, lock-in. Which fits which agency shape.

Divi vs Elementor for Agency Workflows

Divi and Elementor are both legitimate agency picks. The honest comparison: workflow speed, ecosystem depth, hiring availability, client handoff, performance, lock-in, and which fits which agency shape.

Sync a WordPress business directory of thousands of listings to a Google Sheet: a change-only WP-CLI pull command, structured hours, and a closed status that keeps the page for SEO.

Update a WordPress Business Directory From a Google Sheet

Keep a directory of thousands of listings in sync with a Google Sheet a non-dev maintains: a WP-CLI pull command that reconciles each listing change-only, parses structured hours, and flips permanently-closed places to a closed status instead of deleting them.

How to register a custom Gutenberg block with block.json: the metadata-first approach using register_block_type() on the init hook, the file: asset convention, and a @wordpress/scripts build step that ships edit.js and save.js.

Register a Custom Gutenberg Block with block.json

The modern way to register a custom Gutenberg block: a block.json metadata file, register_block_type( __DIR__ . '/build/callout' ) on the init hook, and a @wordpress/scripts build step. One source of truth for PHP and JS.

Six root causes of ACF slowness at scale: object cache, oversized Repeaters, deep nesting, meta_query, field count, field-key overhead. Real fixes.

Common ACF Performance Problems on Large WordPress Sites

ACF performance problems at scale almost always trace to one of six things: missing object cache, oversized Repeaters, deeply nested Flexible Content, meta_query usage on ACF fields, too many fields per post type, or the field-key reference overhead. Here is what to look for and fix in each.

Run a WordPress restaurant menu from a Google Sheet: a menu_item CPT, a WP-CLI service-account pull, change-only writes, and an 86'd sold-out flag that hides instead of deletes.

Manage a Restaurant Menu in WordPress From a Google Sheet

Let the kitchen edit a Google Sheet (prices, sections, daily specials, an 86'd flag) and have WordPress reconcile a menu_item post type to it on a schedule. A WP-CLI pull command, change-only, dry-run, and a sold-out flag that hides instead of deletes.

How to disable jQuery Migrate in WordPress: a wp_default_scripts snippet that removes the jquery-migrate dependency on the front end while keeping it in the admin, with the JQMIGRATE console-warning test that confirms it is safe.

Disable jQuery Migrate in WordPress

How to disable jQuery Migrate in WordPress: remove the jquery-migrate dependency on the front end so the compatibility shim stops loading, plus the testing step that tells you whether it is safe.

ACF Checkbox returns arrays because they're multi-select. The 3 return formats, rendering patterns, query patterns, when to switch to Select or Radio.

Why Your ACF Checkbox Field Returns an Array

ACF Checkbox fields return arrays because they support multiple selections. The shape varies by Return Format. Here's what each option returns, the patterns for rendering and querying, and when to switch to a Select or Radio field instead.

Hardening a custom WordPress REST API write endpoint against unauthorized and replayed requests

Securing a WordPress REST API Write Endpoint

A custom write endpoint accepts changes from the open internet. Harden it step by step: header secret, constant-time compare, HMAC signatures, replay protection, rate limiting, secret out of the repo, and a hidden route.

Divi's Dynamic Content binds ACF fields to module properties. Setup, patterns for Text and Image, limits for Repeater and Flexible Content, escape hatches.

Using ACF with Divi for Dynamic Content

Divi's Dynamic Content lets editors bind ACF field values to module properties without leaving the visual builder. The setup, the patterns that work for Text and Image fields, the limits for Repeater and Flexible Content, and the custom-shortcode escape hatch.

Why UpdraftPlus and other in-WordPress backup plugins fail when the site is compromised, plus a working 3-2-1 setup with restic or borg, retention policy, and a verification routine.

Off-Server WordPress Backups (3-2-1) With Verified Restores

The backup plugin running inside WordPress is the same WordPress the attacker just compromised. A 3-2-1 backup strategy with restic or borg, stored outside the trust boundary, and verified by monthly test restores. Configuration, retention, and the exact restore sequence after a compromise.

Wire ElasticPress to WP_Query so WordPress queries hit Elasticsearch instead of MySQL. Install, indexable post types, ep_integrate, wp-cli index, faceted aggregations, and when ES actually beats MySQL FULLTEXT.

How to Use ElasticPress with WP_Query

Wire ElasticPress to WP_Query so WordPress queries hit Elasticsearch instead of MySQL. Covers installation, indexable post types, ep_integrate, the wp-cli index command, faceted search with aggregations, and when ES actually beats MySQL FULLTEXT.

Clean responsive images from ACF Image fields. ID + wp_get_attachment_image for auto srcset, manual srcset control, picture element for art direction.

Proper Responsive Images with ACF Image Fields

The cleanest pattern for responsive images from an ACF Image field: ID return format plus wp_get_attachment_image, which produces a complete srcset and sizes attribute from registered image sizes. Plus the manual srcset pattern when you need control.

Set WordPress featured images from a spreadsheet of URLs: a WP-CLI command that sideloads each image into the media library and sets it as the post thumbnail, idempotent.

Set WordPress Featured Images From a Spreadsheet of URLs

A sheet of post_id and image_url, a WP-CLI command that sideloads each URL into the media library and sets it as the post's featured image. Change-only, dry-run by default, and it skips posts that already have a thumbnail so re-running never duplicates.

ACF Repeater first row appears blank? Three causes: missing the_row, incorrect sub-field name, accidental data overwrite. Real fixes with code examples.

Why Your First ACF Repeater Row Appears Empty

An ACF Repeater where the first row's data looks blank is almost always one of three things: missing the_row(), incorrect sub-field name, or accidental data overwrite via update_field with the wrong field reference.

How to catch and route your own 404s in WordPress: hook template_redirect, check is_404(), parse the requested path, then resolve to content with status_header(200), 301-redirect with wp_safe_redirect(), or let it fall through to the real 404.

Catch and Route Your Own 404s in WordPress

Intercept requests that would 404 in WordPress on template_redirect, then resolve them to real content with status_header(200), 301 to the right URL with wp_safe_redirect(), or let them fall through. A fallback router for dynamic and legacy slugs you cannot enumerate.

How to register a custom WP-CLI command in WordPress: a command class wired up with WP_CLI::add_command(), public methods as subcommands, args and flags documented with @synopsis, and a make_progress_bar() loop for batch jobs.

Add a Custom WP-CLI Command in WordPress

How to register a custom WP-CLI command: guard it with defined('WP_CLI'), wire it up with WP_CLI::add_command(), turn class methods into subcommands, document args with @synopsis, and show progress with make_progress_bar().

Gravity Forms does not natively submit Repeater-shaped data. Three patterns: append-per-submission, grouped sections, JSON-encoded field. Real code.

Sending Gravity Forms Data Into ACF Repeater Fields

Gravity Forms does not natively submit Repeater-shaped data, but three patterns handle the common cases: append-per-submission, single submission with grouped sections, and a custom JSON-encoded field. Here is each with code.

Useful acf/save_post patterns: derived fields, taxonomy sync, search-index refresh, ACF-to-meta mirroring, validation, audit logging, with code examples.

Useful Things You Can Do with acf/save_post

acf/save_post is the hook that fires after ACF saves a post's custom fields. Useful patterns: derived field computation, taxonomy sync, search-index refresh, ACF-to-meta mirroring, validation, audit logging. Plus the gotchas.

Elementor Pro's ACF Dynamic Tags. Every field type, Loop widget for Repeater, Theme Builder integration for archives. The mature reference.

Using ACF with Elementor Dynamic Fields

Elementor Pro's Dynamic Tags integration with ACF is the mature reference for binding custom field values to widget properties. Coverage for every field type, including Repeater via the Loop widget, and the Theme Builder integration for archive templates.

The four ways attackers silently disable Wordfence, Sucuri Security, iThemes Security Pro (Solid Security), Patchstack, MalCare, and Jetpack Scan. Plus the above-doc-root attack class where the malware lives outside WordPress and no plugin can ever see it. Server-side monitoring that doesn't depend on WordPress being trustworthy.

Why Wordfence (or Any Security Plugin) Keeps Getting Silently Disabled

WordPress security plugins running inside WordPress can be disabled by anything that runs inside WordPress, including the malware they're supposed to catch. The four mechanisms attackers use to silently turn off Wordfence, Sucuri, Jetpack, WP Activity Log, and similar tools, plus the server-side monitoring layer that doesn't depend on WordPress being trustworthy.

Push one change to a fleet of WordPress installs from one Google Sheet: group rows by a website column, fan each batch out to its site, with partial-failure handling.

Manage Multiple WordPress Sites From One Google Sheet

Run a fleet of WordPress installs from a single Google Sheet: a website column per row, a small controller that groups the rows by site and pushes each batch to that site's endpoint, and partial-failure handling so one dead site never blocks the rest.

ACF Flexible Content + template parts + a shared library is a lightweight component system for WordPress. Patterns and comparison to React alternatives.

Using ACF Like a Lightweight Component System

ACF Flexible Content plus template parts plus a shared component library is effectively a lightweight component system for WordPress. The patterns: one component per layout, props via sub-fields, composition over inheritance, and how it compares to React-based alternatives.

Get alt text from an ACF Image field. Per return format: Image Array, Image ID, Image URL. The cleanest always-correct pattern for accessible output.

How to Get ALT Text from an ACF Image Field

Getting alt text from an ACF Image field depends on the field's Return Format. Image Array gives you the alt directly. ID and URL formats need a wp_get_attachment helper. Plus the cleanest pattern for always-correct alt output.

Run a scheduled WooCommerce sale across thousands of products from a Google Sheet: a WP-CLI command sets sale price and on-sale dates via the CRUD. Dry-run, change-only.

Bulk Schedule WooCommerce Sale Prices From a Google Sheet

Run a site-wide or category sale from a sheet of sku, sale_price, start and end dates. A WP-CLI command reads the sheet, sets scheduled sale dates through the WooCommerce CRUD, and lets WooCommerce flip and unflip the price on its own. Dry-run and change-only.

Naming conventions for ACF field groups, field names, and field keys that keep a multi-year codebase navigable. Real patterns from directory-scale work.

ACF Field Naming Conventions That Actually Scale

A few naming conventions for ACF field groups, field names, and field keys keep a multi-year codebase navigable: snake_case names, hierarchy-encoded keys, post-type prefixes, sub-field consistency. The cost is zero; the benefit compounds.

Using Git with a WordPress project: what to track, what to ignore, and how to deploy.

How to Use Git with WordPress

How to put a WordPress project under Git: what to track vs ignore, a ready .gitignore, version-controlling just your theme or plugin, and deploy options.

A complete hardened wp-config.php template for WordPress with comments on every setting: DISALLOW_FILE_EDIT, FORCE_SSL_ADMIN, salt rotation, file permissions.

A Hardened wp-config.php Template (with Comments on Every Choice)

wp-config.php is the first PHP file WordPress loads. The defaults from the stock installation are minimal; the hardened defaults take five minutes to apply and close most of the attack surface that lives below the plugin layer. A complete annotated template covering disabled file editing, forced HTTPS, secure salt rotation, debug behavior, and the file permissions that matter.

Sync WordPress and a Google Sheet in both directions with a service account and spreadsheets.values.batchUpdate: write WordPress back to the sheet, pull edits back, and resolve conflicts with per-field ownership instead of last-write-wins.

Two-Way Sync Between WordPress and a Google Sheet

Keep a Google Sheet and WordPress in step in both directions: WordPress writes its current state back into the sheet, and edits in the sheet flow back to WordPress. The plumbing is easy. The hard part is deciding which side wins, and this is how to decide it on purpose instead of by accident.

Set static defaults in the field group, dynamic defaults via acf/load_value, multi-select array defaults, and behavior when the default does not match.

How to Set Default Values in ACF Select Fields

ACF Select fields have a Default Value setting in the field group editor that handles the simple case. For dynamic defaults (computed from another field, role-based, or per-post-type), the acf/load_value filter is the right tool.

Canonical guide to update_field(): signature, Repeater and Flexible Content writes, field keys vs names, options pages, user meta, and the gotchas.

How to Update ACF Fields Programmatically

update_field() is the canonical way to write ACF data programmatically: the function signature, how to write to Repeater and Flexible Content fields, when to use field keys instead of names, options pages and user meta, and the gotchas that bite.

Tuning WP_Query memory and query count at scale in WordPress with fields ids, cache control, batching, and flush-in-loop

WP_Query at Scale: Performance and Memory

Why a WP_Query over thousands of posts balloons on memory and queries, and the exact knobs (fields => ids, no_found_rows, cache priming, batching, flush-in-loop) that fix it, each one measured before and after.

Map Gravity Forms field submissions to ACF fields. gform_after_submission hook pattern, dedicated GF+ACF plugin, field-mapping conventions.

How to Populate ACF Fields from Gravity Forms

Gravity Forms can create WordPress posts on submission, but mapping form fields to ACF fields requires a hook. The canonical pattern uses gform_after_submission to call update_field. Plus the dedicated Gravity Forms + ACF plugin path.

Three patterns for counting ACF Repeater rows: count() on the raw field, get_field_count, and a fast meta-only count without loading the rows.

How to Count Rows in an ACF Repeater Field

Counting ACF Repeater rows is three short patterns: count() on the raw field, get_field_count() inside a loop, and a faster meta-only count that skips loading the rows. Each has its right use case.

ACF True/False stores 1 or 0 but appears as boolean in PHP. Four causes of unexpected behavior: loose comparison, defaults, conditional logic, meta_query.

Why ACF True/False Fields Sometimes Behave Unexpectedly

ACF True/False fields store 1 or 0 in the database but appear as booleans in PHP. Loose-comparison bugs, empty-row defaults, conditional logic visibility, and meta_query gotchas are the four causes of unexpected behavior. Here is the fix for each.

Running a safe bulk custom-field update in WordPress with a dry run, backup gate, and change-only changelog

Safely Bulk-Update Custom Fields in WordPress

A bulk custom-field update with no undo is one typo away from wrecking thousands of posts. Here is a safe pattern (and a downloadable WP-CLI command) with a dry run, a backup gate, a change-only changelog, and idempotent writes.

ACF Options Pages for site-wide settings: header CTAs, footer links, social profiles. Registration, reading patterns, parent/child structure, cache strategy.

Using ACF Options Pages for Global Site Settings

ACF Options Pages are the right home for site-wide settings: header CTAs, footer links, social profiles, contact info, global announcements. Registration, reading patterns, parent/child structure, and the cache-busting trick for high-traffic sites.

Filter ACF Relationship by post type via field setting; filter dynamically via acf/fields/relationship/query for taxonomy, status, and ACF-field conditions.

How to Filter ACF Relationship Fields by Post Type

ACF Relationship fields let editors pick from any post type by default. The Post Type filter in the field group editor restricts the choice list. For dynamic filtering (taxonomy, status, ACF field), the acf/fields/relationship/query filter is the right tool.

Thousands of hits to admin-ajax.php are usually WordPress Heartbeat and plugins, not an attack. How to read the action parameter and the fix that does not break your site.

admin-ajax.php High Traffic: Attack or Normal?

Thousands of hits to wp-admin/admin-ajax.php are almost always your own site: WordPress Heartbeat and plugins, not a DDoS. How to read the action parameter, when it is a real attack, and why blocking the file breaks your site.

Comparing Advanced Custom Fields with native WordPress post meta, and how both store data in the same wp_postmeta table

ACF Fields vs Native Post Meta in WordPress

ACF and native post meta both write to the same wp_postmeta table. Here is what register_post_meta gives you, what ACF adds on top, and the read/write rules so a bulk script and a content editor never fight over the same field.

Step-by-step WordPress malware removal: identify the attack vector (files, database, .htaccess, wp-config), clean every layer, rotate credentials, and lock down to prevent reinfection. Cross-platform scripts for Linux and macOS.

How to Remove WordPress Malware: The Practitioner's Playbook

A step-by-step methodology for finding and removing malware from a compromised WordPress site, written by a Security+ certified engineer who's been cleaning sites since the early WordPress 2.x era. Covers every attack vector: file backdoors, database injections, .htaccess hijacks, wp-config tampering, and recurring reinfection. Originally written in 2016, updated regularly as new patterns emerge.