Blog

  • Premium Plugin Licenses: The Updates That Stop Quietly

    Premium Plugin Licenses: The Updates That Stop Quietly

    A lapsed license does not break a site.

    It does something quieter: it stops the updates.

    Short answer: A premium plugin keeps running after its license ends, but WordPress plugin updates from the vendor, including security fixes, can stop arriving. To find out where you stand, list every installed plugin, work out where each one gets its updates from, read the license record each vendor’s system left on the site, and sort every plugin into a clear state. When a state cannot be read, call it unknown. Never call it fine. That is the order the Premium License Expiry playbook in the Protuno dashboard follows, and it is a check you can run by hand.

    What this post is, and is not: A practical way to find out which premium plugins on a site have lost their update feed, and how to word what you find. It is not a ranking claim, not legal advice about licensing, and not a result from a real run: no playbook was run for this post, and every visual is a labelled illustration. The step names and rules come from the playbook as the Protuno dashboard shows it. The playbook is Beta and read only.

    Free plugins from the WordPress directory update from one place. Paid plugins update from many.

    Illustration of two update routes on one WordPress site: free plugins updating from the directory, and premium plugins updating through each vendor's own license
    Two update routes on one site. A lapsed license cuts the second one.

    Why A Lapsed License Is Easy To Miss

    Nothing visible happens on the day a license expires. The plugin still loads. The pages still render. The client sees no difference.

    What changes is the supply. A WooCommerce.com subscription, for example, is described by WooCommerce as giving you access to product updates and support for the duration it covers, billed annually or every two years. When that period ends without a renewal, the entitlement goes with it.

    Freemius describes licenses in similar terms: they control access to premium features based on the plan, the billing cycle and the number of allowed activations. Easy Digital Downloads Software Licensing works through its own license records too. Each vendor keeps a different record, in a different place, in a different shape.

    That is the real problem. There is no single license screen for a WordPress site. There is one screen per vendor, and nobody opens six of them on a Tuesday.

    The risk is not the expiry itself. It is the length of time between a security fix being published and anyone noticing the plugin never received it. WordPress 7.1.3, a security release with 7 security fixes, came out on 6 October 2026. Core has its own update path. A premium plugin with a lapsed license may get nothing from its vendor.

    Three Places A Plugin Gets Its Updates

    SourceHow the site learns about updatesWhat a lapse looks like
    The WordPress directoryCore checks the directory and records what it findsNot a license issue. A plugin pulled from the directory is a different problem
    The vendor’s own updaterThe plugin asks the vendor’s server, using a license keyThe key stops working. The plugin runs, and the update offer disappears or fails
    No updater at allNothing feeds itThe plugin was installed from a zip and never checks for anything

    The third row is the dangerous one, because it looks exactly like a healthy site. A plugin that has no updater does not show an error. It just never shows an update.

    Illustration of an inventory table with each plugin's name, version, active state and update source, two rows showing no update source
    An inventory that names the update source makes the silent rows visible.

    A Small Test You Can Run Today

    Open the plugin list on one site. For each plugin you did not get from the WordPress directory, write down:

    • The plugin name and version.
    • Whether it is active.
    • Which company sells it, and where you log in to see its license.
    • The expiry date that account shows, or “no account found”.
    • Whether the Updates screen has ever offered an update for it.

    Then ask one blunt question of every row: if this vendor published a security fix this morning, would this site hear about it?

    A rough way to see what WordPress itself knows about a site’s plugins, from a terminal with WP-CLI:

    wp plugin list --fields=name,version,status,update

    The update column shows what the site currently believes. It cannot tell you why a plugin shows nothing. A blank can mean up to date, or it can mean nothing is checking. That ambiguity is the whole point of this post.

    The Playbook, Step By Step

    Premium License Expiry is a Protuno Maintenance playbook. In the Protuno dashboard it carries a Beta label, is read only and runs seven steps, plus a Brief and Schedule screen. The public playbooks page lists a playbook with the same job under the shorter name License Expiry, described as warning before a paid plugin stops receiving security updates, and shows four steps. This post follows the dashboard’s names and seven steps. Check which version your account shows.

    #StepWhat it does
    1Enumerate Plugins And Update SourcesMakes one read-only call that lists every installed plugin with its name, version, active state and update source. A plugin that is installed but absent from the update records has no updater feeding it. It stops if the core plugin and update records cannot be read
    2Scan EDD LicensesReports the cached Easy Digital Downloads license records that really exist in the site’s options: a prefix, a status, an expiry and whether a key is present. No records is a normal, clean result. It never prints a key
    3Scan Freemius LicensesReports the cached Freemius license state: module, plan, expiry date, and whether it is lifetime or expired. A lifetime license is never reported as expired
    4Scan WooCommerce SubscriptionsReports whether the site is connected to WooCommerce.com, and for each subscription its product, expiry date, and whether it is active, expiring or expired
    5Classify Each PluginGives every plugin one class, with a confidence level and the evidence behind it
    6Optional Live License RecheckMakes no remote call. It needs an approval that a playbook has no interactive step for, and a vendor store address that standard WordPress options do not store. It will not guess one
    7License Expiry ReportTurns the classes into one report with a status of ok, warn or stopped, headlining the dangerous states first

    The order is the safety. Nothing is written at any step, and nothing is called healthy because it was not found broken.

    A 30-second walk-through of the seven steps. All example values are illustrations, not a real run.

    Seven Classes, And What Each One Means

    ClassPlain meaning
    validA license record was read and it is current
    expiring-soonA record was read and the expiry is close
    expired-still-runningThe license has ended, the plugin is still active. Updates are off, but the plugin works
    lapsedThe license has ended in a way the record marks as lapsed
    lifetimeA record was read and it has no expiry
    nulled-suspectA signal that may point to an unlicensed or tampered copy. Worded as “possible unlicensed or nulled copy (confidence: low, medium or high), needs verification”
    unknownThe state could not be determined

    Two of these deserve a pause.

    unknown is the honest class. The playbook’s rule is that an ambiguous or absent status is unknown, never valid, and that a vague match is never asserted as valid. A plugin the system cannot read gets reported as “unknown (not determined)”. It does not get the word fine.

    nulled-suspect is the class that can do harm if it is worded carelessly. A weak signal, for example a name with “Pro” in it and no entry in the update records, is not evidence of anything. Many paid plugins never appear in those records until their own updater has run. So every nulled-suspect line carries a confidence level, and a low-confidence line is a prompt to verify, never an accusation.

    What The Report Treats As Urgent

    FindingConfidenceHow it is treated
    expired-still-runningHigh, because a license status was actually readHeadline. The plugin works, but security updates are off
    lapsed or expiring-soonRead from a recordWarning
    nulled-suspectMedium or highWarning, worded as a possible unlicensed copy that needs verification
    nulled-suspectLowListed under “Needs verification”. Never urgent
    unknownNot determinedReported as unknown. Does not change the status

    For a low-confidence line, the advice is deliberately mild: open the plugin’s own license or settings page and confirm the license is active. If it is, there is nothing to do. The report does not tell you to remove, replace or switch a plugin on a weak signal.

    The status follows the same logic. The report is warn when any plugin is expired-still-running, lapsed or expiring-soon, or when a nulled-suspect has medium or high confidence. Low-confidence rows alone leave the status at ok, because otherwise every site whose connector plugin updates itself would be flagged.

    What A License Check Does Not Solve

    • Whether the plugin is any good. A valid license on an abandoned plugin is still an abandoned plugin. Our post on what a removed plugin leaves behind covers that side.
    • Whether a plugin has a known vulnerability. A license is about the update supply. Matching installed versions against vulnerability data is a different check, covered in vulnerability monitoring.
    • Vendor records the site does not keep. The playbook reads cached records on the site for Easy Digital Downloads, Freemius and WooCommerce.com. A vendor with its own system, or an account that only lives on the vendor’s server, is not covered by those steps.
    • A live answer from the vendor. Step 6 makes no remote call. What you get is what the site last recorded, not today’s answer from the vendor’s server.
    • Renewals. It reports. It never renews, updates or changes a license.
    • Sites that cannot run PHP. If the check cannot read the site’s own records, it says so and stops. It does not infer a license state from anything else.

    Which Check Are You Actually Running?

    If you need to knowThe evidenceNext action
    Whether a paid plugin is still being updatedThe update source and the license record, side by sideRenew, or plan a replacement
    Whether an installed plugin has a known security holeVersion against vulnerability dataRead the vulnerability monitoring approach
    Whether an update is safe to applyA backup, a test and a rollback pathSee how a safe update playbook is written down
    Whether the license cost belongs in the care planThe number of paid plugins and their renewal datesSee care plan economics

    A Five-Plugin Audit You Can Do This Week

    Pick five premium plugins across your busiest client sites. For each, record:

    1. The vendor and the account that owns the license. Is it your account, the client’s, or a former developer’s?
    2. The expiry date, or “not found”.
    3. The date of the last update the site actually received.
    4. Whether the plugin is active and whether anything depends on it.
    5. Who gets the renewal email, and whether that address still works.

    Then pick one outcome per plugin: renew now, move the license to the right owner, replace the plugin, leave it with a dated note, or confirm the license state with the vendor.

    The fifth question is the one that bites. A renewal notice that goes to a former contractor’s inbox is how a license lapses without anyone deciding to let it. Writing the owner and the renewal date into the maintenance contract is a small change that closes it.

    If A License Has Already Lapsed

    Do these in order:

    1. Take a backup, and make sure it restores.
    2. Check whether the vendor has published updates since the license ended.
    3. Renew, or schedule the plugin’s replacement.
    4. After renewing, re-read the Updates screen and confirm the offer comes back.
    5. Test the update on a copy before applying it live.

    If an update goes wrong, a clear undo is what matters. Our post on what an automatic rollback really reverses is a good test of any undo claim. If you suspect a plugin has already been used against the site, start with the first hour after a hack and ignore this post.

    Two Outside Views

    A tutorial and a forum thread. Neither is an endorsement.

    Presslyte’s tutorial on updating WordPress plugins safely walks through a five-step routine: back up, review, update one plugin, check the site and repeat. It also says that if the update source or support status is unclear, you should stop and verify it before going on.

    A beginner tutorial on updating WordPress plugins safely: back up, review, update one plugin, check the site and repeat.

    Presslyte, “How to Update WordPress Plugins Safely” on YouTube, 2 Sep 2026

    On Reddit, an r/Wordpress thread asks whether people wait for Black Friday to buy yearly plugin licenses, and the replies talk about monthly versus annual plans, lifetime deals and whether a first-year discount is worth it. It is a useful reminder that every yearly license is also a renewal date someone has to own.

    “A lot of plugin vendors discount only the first term, and year two renews at full price.”

    r/Wordpress, “Are you waiting for BlackFriday/CyberMonday with plugin purchases?”, September 2026

    Where Protuno Fits

    In the Protuno dashboard, Premium License Expiry sits with the Care playbooks. It is a Beta playbook that only reads, so it does not change a plugin, renew a license or print a key. It needs the Protuno plugin on the site first, and on the one connected site in the dashboard it is not switched on, so there is no run to show here.

    It does not decide whether to renew, and it does not replace asking the vendor. If a site is behind on core, update it first: WordPress 7.1.3, a security and maintenance release, came out on 6 October 2026.

    Sources And Further Reading

    The Point

    A lapsed license does not announce itself. The plugin keeps working, and the updates stop.

    List every plugin, find where each one gets its updates, read the license records, and write unknown when you cannot tell.

    Which premium plugin on your busiest site has no renewal date written down anywhere?

  • WordPress 404 Errors: Which Ones Actually Cost You

    WordPress 404 Errors: Which Ones Actually Cost You

    A client opens a plugin dashboard and sees 4,000 logged 404 errors. They forward the screenshot with one line: “is this bad?”

    The honest answer is that the number on its own means almost nothing. A 404 log is not a defect list. It is a list of requests, and requests come from everywhere: a scanner probing for /wp-admin/setup-config.php, a dead link in a five year old forum post, a typo in an email newsletter, a page the client deleted on purpose last week, and one URL that used to receive real visitors every day.

    Those five things have nothing in common except a status code.

    Short answer: sort 404 errors by what they were connected to, not by how many there are. A URL that had organic traffic, has an external backlink, or is still linked from your own navigation costs you something real and needs a decision. A URL that only ever appeared in bot logs costs you nothing and should stay a 404. Redirect the first group to the closest genuine equivalent, leave the second group alone, and use 410 for pages you deliberately removed.

    What this post is, and is not: this is a triage routine for a 404 log you already have, using data from your own analytics, your own Search Console property and a crawl of your own site. It is not a claim that fixing 404s raises rankings on its own, and it does not measure how any search engine weighs a 404 against a 410.

    Diagram sorting a pile of WordPress 404 errors into four buckets, with bot noise as the largest group by a wide margin.
    Illustration. The pile is big. The part that costs you anything usually is not.

    Why The Count Is The Wrong Number

    A 404 is the correct response to a request for something that does not exist. The status code is doing its job. Treating every one as a defect means treating your server’s correct behaviour as a bug.

    The count feels alarming because 404 logging plugins record everything, including traffic that was never going to be a visitor. Vulnerability scanners walk a list of known paths against every WordPress site they can reach. A site with no inbound links at all will still pick up thousands of 404s a month from that alone.

    Compare two lines from the same log:

    /wp-content/plugins/revslider/temp/update_extract/

    /2024/03/our-pricing-changed/

    The first is a scanner. Nobody linked to it, no human requested it, and redirecting it only adds a rule your server evaluates on every request from then on.

    The second was a real page. If it had traffic or links, it is losing both right now. That is one of the four thousand, and it is the whole job.

    Part of this is already handled for you. WordPress tries to guess the intended address for a missing URL by matching the slug, which is what redirect_guess_404_permalink() does. So a lazy typo in a post slug can resolve without a rule. A deleted page, a changed permalink structure or a renamed category usually cannot, and those are the ones to look for.

    The Four Buckets

    Every 404 in your log belongs in one of four buckets. Sort in this order and stop as soon as a URL qualifies.

    BucketTestCost if ignoredAction
    Had organic trafficThe URL appears in analytics or Search Console data for the period before it brokeLost sessions and any conversions on that pathRedirect to the closest genuine equivalent
    Has external backlinksAnother site links to itLost referral traffic and a wasted inbound linkRedirect, even if traffic was low
    Linked internallyYour own pages, menus, sitemap or templates still point at itVisitors hit a dead end and crawlers waste requestsFix the link at the source, then decide on a redirect
    Bot noiseNone of the above applyNothingLeave it as a 404

    The order stops you overthinking. A URL with a backlink gets a redirect whether or not it ever had traffic, because the link is an asset you already own. A URL with no traffic, no links and no internal references is not a problem to solve, however many times it appears.

    The fourth bucket will usually be the overwhelming majority. That is the expected result, not a sign you checked wrong.

    Getting The Data For Each Test

    Each bucket needs a different source, and none of them is the 404 plugin itself.

    Traffic history. Your analytics property holds the before picture. Export the landing pages that received organic sessions over a window long enough to cover the period before the URL broke, then intersect that list with your 404 list. Anything in both is bucket one. If analytics was reinstalled or duplicated at some point, the numbers for that period may be wrong in ways that are easy to miss, which the duplicate Google Analytics tag post covers.

    Backlinks. This is the one piece you cannot derive from your own site. You need a backlink source, and the result will be incomplete whichever one you use, because no index sees every link. Treat the result as a floor, not a total.

    Internal links. This one you can answer completely, and you should, because it is the only bucket that is entirely your own doing. Crawl your own site and collect every internal link that returns a 404. The WordPress crawl audit post is about what only a crawler can find, which is exactly this job. A crawl also exposes the opposite failure, pages that exist but nothing links to, which the orphan pages post covers.

    Sitemap entries. A 404 listed in your sitemap is a specific and avoidable mistake, because you are actively submitting a dead URL for crawling. Pull the sitemap, request each URL and flag every non-200 response. The sitemap audit post has the commands.

    The links you control are the easy half of this. The broken link checker post explains why a plain 404 check also misses soft 404s and redirect chains, so this post leaves those two alone.

    410 Versus 301, And When To Use Neither

    Once a URL is in a bucket you have three options, and the choice is less about search engines than most arguments about it suggest.

    ResponseMeansUse it when
    301This has permanently moved hereA genuine equivalent exists for what the visitor wanted
    410This is gone on purpose and is not coming backYou deliberately removed the page and there is no equivalent
    404This does not existEverything else, including all bot noise

    The rule that keeps this clean: a 301 is a promise that the destination answers the request the visitor made. Redirecting a deleted product page to your homepage does not do that. The visitor wanted a product and got a homepage, which is a dead end wearing a 200 status code. It is worse than the 404, because the 404 at least told the truth. Google’s own 301 redirect guidance is about pointing to the new location of the same content.

    So the test for a redirect is not “do I have somewhere to send this”. It is “does that destination give the visitor the thing they came for”. If no page does, use 410 for deliberate removals and leave the rest as 404.

    The MDN reference for 410 Gone describes it as a deliberate, permanent removal, and suggests 404 when you do not know whether the removal is permanent. That is a good rule of thumb. A 410 is a cleaner statement than a 404 that could be a temporary failure. It is not required, and a site that only ever uses 404 is not broken.

    Decision diagram choosing between a 301 redirect, a 410 response and leaving a WordPress URL as a 404.
    Illustration. Three outcomes. Most URLs take the third branch.

    One habit to avoid: mass redirecting everything in your log to a single destination. It turns a readable report into noise, adds rules that run on every request, and when you later redirect something properly you risk building a chain of hops instead of a single one.

    A walkthrough of the Redirection plugin: installing it, setting up 301 redirects and tracking 404s in its error log.

    JOSHUA, “How to Fix 404 Errors Using the Redirection WordPress Plugin (Easy Guide)” on YouTube, 10 Jul 2026

    “…I am thinking of changing the response to “410 Gone” to see if they go away permanently.”

    r/TechSEO, “404 Not Found or 410 Gone?”, October 2026

    Both are about the same decision. The video covers the mechanics of setting a redirect up in a plugin, and it does not tell you which URLs deserve one. The thread is a working question about whether answering 410 clears long-gone URLs out of Search Console, and the line quoted above is the one this post answers: did the URL ever cost anything, and is there a real equivalent?

    The 404 Log Is A Security Signal Too

    Scanner paths are not an SEO problem. They are still information.

    A sharp rise in requests for paths that never existed on your site means someone is probing. You do not need to act on each line, but you should record the rise and compare it with what changed that week. If it lines up with a plugin install or a published vulnerability, that is worth a look. The security monitoring playbook post is the place to start for that side of it.

    Keep the two uses apart. If scanner volume and real regressions feed the same alert, the volume will bury the regression every time.

    Monitoring It Without Drowning In It

    Triage once and you have cleaned up a backlog. The useful version catches the next one within days instead of at the next audit.

    Two signals are worth watching, and they are not the same signal.

    The first is a URL that used to return 200 and now returns 404. That is a regression, and it is the only 404 event that deserves an alert. You detect it by holding a list of your own known-good URLs and requesting them on a schedule, not by watching the log.

    The second is a rise in 404s against URLs that never existed. That is scanner activity, covered above.

    A small test you can run today:

    • Export the URLs from your sitemap and from your main navigation.
    • Request each one and record the status code, the final URL after redirects and the date.
    • Re-run it after every core, theme or plugin update, and after any permalink change.
    • Treat any URL that moved from 200 to 404 as an incident, and everything else as housekeeping.
    # status and final URL for one address
    curl -s -o /dev/null -L -w "%{http_code} %{url_effective}\n" https://example.com/pricing/

    Staging drift is a common cause of the 200 to 404 jump, because a permalink or plugin setting that differs between staging and live changes which URLs exist.

    What 404 Triage Does Not Solve

    Sorting your 404s is housekeeping with a clear payoff. It is not an SEO strategy, and it proves little on its own.

    It cannot tell you whether:

    • the page that was removed should have been removed, which is a content decision nobody can make from a status code;
    • the redirect you added serves the visitor’s intent, since a 301 to a loosely related page still fails the person who clicked;
    • a ranking change followed the fix, because too many other things move at once to attribute it;
    • your backlink data is complete, since every index is partial;
    • the traffic a broken URL used to get would return if you restored it, as demand may simply have moved on; or
    • a URL returning 200 is actually working, because a page that renders nothing but returns 200 passes every status check you run.

    That last one is the real gap. Every test here is built on the status code. Catching a page that answers 200 and shows nothing needs a check that looks at what the page contains. Google describes these as soft 404s in its HTTP status code documentation.

    The other limit is scope. This routine is about URLs that are gone. It says nothing about URLs that exist, are reachable and are still not doing anything useful.

    Fixing In The Right Order

    1. Take a restore point first, so a bad redirect rule can be undone.
    2. Fix internal links at the source. A redirect that hides your own broken link is a patch, not a fix.
    3. Redirect the traffic and backlink URLs one to one, each to its closest genuine equivalent.
    4. Remove dead URLs from the sitemap.
    5. Mark deliberate removals as 410.
    6. Re-request every changed URL and confirm one hop, then re-run the known-good list.

    Sources And Further Reading

    For the wider technical review this belongs to, see the Technical SEO Audit playbook post and the playbooks page.

    How Many Of Your Sites Have A 404 Log Nobody Has Read?

    Most agencies have a 404 log on every site and a triage habit on none of them, because the counts are large and the signal is buried.

    Pick three sites. For each one, pull the unique 404 URLs, intersect them with analytics and a crawl, and write down two numbers: how many had any cost attached, and how many were scanner noise.

    So, on the sites you run: when a client next asks whether four thousand 404s is bad, can you answer with the handful that actually cost them something, or only with the four thousand?

  • WordPress On-Page SEO Checklist, Step By Step

    WordPress On-Page SEO Checklist, Step By Step

    A green score is an opinion about one version of a post.

    It is not a reading of the page a visitor or a crawler actually receives.

    Short answer: Check one post at a time, in a fixed order. Find out which plugin owns the title and description. Read what is stored. Read what the live page serves. Compare both against a short list of limits: title, description, one H1, alt text, internal links and schema. Write each fix as a before and after. Change only what you can undo, then read it back. That is a WordPress on-page SEO checklist you can run by hand, and it is the same order the AI SEO Optimiser playbook in the Protuno dashboard follows.

    What this post is, and is not: A practical checklist for one post at a time, with the limits we use and the reasons for them. It is not keyword research, not a ranking promise and not a result from a real run: no playbook was run for this post, and every visual is a labelled illustration. The step names and rules come from the playbook as the Protuno dashboard shows it. The playbook is Beta.

    An audit tells you what is wrong. A diff tells you exactly what changed.

    Illustration of the six on-page checks on one WordPress post: title, description, H1, image alt text, internal links and schema
    Six things to read on one post. Everything else is a different job.

    Why A Green Score Is Not A Checklist

    An editor score is built from the post as you are writing it. The page that goes out is built later, by your theme, your plugins and your cache.

    That gap is where on-page problems hide. A theme can print its own H1. A second plugin can output a different title tag. A caching layer can serve last week’s version. The score cannot see any of that.

    Google adds another layer. Its documentation says title links are created automatically from several sources: the title element, the main visual title, heading elements such as the H1, og:title tags, anchor text and more. The title you type is one input, not the verdict.

    Snippets work the same way. Google says they are primarily created from the page content, and that it sometimes uses the meta description when it describes the page better. So a description is a pitch you offer, not a result you control.

    A good check looks at what is served, and says what it cannot see.

    Three Places The Same Post Lives

    ViewWhat it tells youWhat it cannot prove alone
    Stored (the database and the editor)The title, description and body as saved, plus the image tags in the bodyWhether a theme or plugin changes any of it before it is served
    Rendered (the live page)The title tag, description, H1 count and schema a crawler really receivesWhether you saved what you meant to, or whether a cache is stale
    Search result (Google)The title link and snippet Google chose to showAnything about your page. Google decides this, and it can differ by query

    The first two you can check today. The third you can only observe, and it changes after Google recrawls the page.

    Illustration of one WordPress post seen three ways: stored in the editor, rendered on the live page, and shown as a search result
    Same post, three versions. A good check compares the first two.

    A Small Test You Can Run Today

    Pick one published post. Write down:

    • The post URL and which SEO plugin is active (Yoast, Rank Math, All in One SEO or none).
    • The title as served, and how many characters it is.
    • The meta description as served, and how many characters it is.
    • How many H1 tags the live page contains.
    • Which images have no alt text, or an empty one.
    • How many links in the body point to your own site.
    • Whether the page already has a JSON-LD schema block, and which plugin prints it.

    A rough way to read the served page from a terminal:

    URL="https://example.com/your-post/"
    curl -s "$URL" -o post.html
    grep -o -i '<title>[^<]*' post.html
    grep -o -i '<meta name="description"[^>]*' post.html
    grep -o -i '<h1' post.html | wc -l
    grep -o -i '<img[^>]*>' post.html | grep -v -i 'alt="[^"]'
    grep -o 'application/ld+json' post.html | wc -l

    These lines read the served HTML only. A tag that a script adds in the browser will not show up, and the attribute order in the description tag varies, so if nothing prints, open view-source and look. The image line lists every image with a missing or empty alt, which includes decorative ones. Treat the output as a prompt to look, not as a score.

    The Playbook, Step By Step

    AI SEO Optimiser is a Protuno SEO playbook. In the Protuno dashboard it carries a Beta label, runs eleven steps in a fixed order and can change a site: it rewrites one post’s content and SEO fields. The public playbooks page lists a playbook with the same purpose under the name Page Optimise, described there in five steps. This post follows the dashboard’s names and eleven steps. Check which version your account shows before you rely on either.

    #StepWhat it does
    1Resolve Target And GatewayReads one setting, the target post ID. If nothing is set it stops. It never picks a post for itself. Confirms the post exists and is a published post or page
    2Detect SEO FrameworkLooks for Yoast, then Rank Math, then All in One SEO. Yoast and Rank Math have writable title and description fields. With All in One SEO or no plugin, those two are deferred
    3Read On Page StateReads the stored title and length, description and length, H1 count, heading outline, images without alt, internal link count and any inline JSON-LD
    4Read The Live PageFetches the rendered page to see the served title, description, H1 count and JSON-LD. If it cannot, it says so. Core Web Vitals are not available here
    5Keyword And SERP ContextOptional. Only if a target keyword is set, it looks up the top ten results for title and snippet context. Otherwise it skips
    6Diagnose On Page SEOTurns the reads into findings tagged by severity
    7Compose Proposed FixesWrites the exact title, description and image alt text, and an Article schema block only if none exists. Every value is checked against length limits first
    8SEO Diff Approval GateBuilds before, after and reason rows. Drops anything out of bounds or unverifiable
    9SEO Safety SnapshotRecords the current title, description and a hash of the body in a backup field before anything is overwritten
    10Apply SEO FixesWrites to the one target post only. The body goes through a recorded content update. Title and description use a recorded meta write when the site offers one
    11Recheck And SEO ReportReads every changed field back, checks the rendered page, and reports before and after with the exact undo

    The order is the safety. Nothing is written before a snapshot, and nothing is called done before it is read back.

    A 31-second walk-through of the eleven steps. All example values are illustrations, not a real run.

    Read The Gate Carefully

    Step 8 is named an approval gate. The instructions for that step say plainly that this playbook has no interactive human step, and that the steps after it, the snapshot, the recorded write and the read-back, stand in for one. The public Page Optimise listing describes applying on approval. Those are two different promises. Before you switch a playbook that writes to a client post, find out which one your version makes, and decide whether you are comfortable with it.

    The Limits It Checks Against

    FieldWhat it flagsSeverity
    Meta descriptionMissingHigh (low if the site has no SEO plugin, since there is nowhere to store one)
    Meta descriptionOver 160 characters, or 1 to 69Medium for too long, low for too short
    TitleOver 60 charactersMedium
    TitleUnder 15 charactersLow
    H1None, or more than oneMedium
    ImagesAny inline image without alt textMedium
    Internal linksNone in the bodyLow
    SchemaNever adds a block if one already exists, inline or plugin-renderedNot a finding

    These are the playbook’s bounds, not Google’s rules. Google’s own pages say there is no limit on how long a title element or a meta description can be, and that Google shortens what it shows to fit the device width. Treat 60 and 160 as a practical editing target, not as a ranking threshold.

    One more limit matters. The image rule counts an image with an empty alt as missing. The W3C accessibility guidance says the opposite for purely decorative images: they should have an empty alt on purpose. A sweep like this will flag them anyway, so a human has to sort real gaps from decoration. Our alt text audit shows how easily an alt count can mislead.

    What An On-Page Check Does Not Solve

    • Whether anyone searches for the topic. Nothing here does keyword research or judges search intent.
    • Whether the content is good. A tidy title on a thin page is still a thin page.
    • Rankings. No set of fixes to one post guarantees a position. Core Web Vitals are not available to this playbook either.
    • The H1 and the links. The playbook reports them and leaves the change to a person. Restructuring a heading or adding links needs judgement.
    • Image alt text that lives on the media record instead of in the post. It is reported, not written.
    • The rest of the site. It acts on one post. For site-wide patterns, the technical SEO audit reads the whole site and changes nothing.

    Questions To Ask Your Current Tooling

    • Does it read the live page, or only the editor?
    • Which plugin owns the title and description, and what happens if two are active?
    • Does it change anything on its own, and where is the target post chosen?
    • Is there a record of the old value before a new one goes in?
    • How do you undo a change that went wrong, and does the undo cover the title as well as the body?
    • Does it tell you what it could not check, or does it return a clean score?

    Which Check Are You Actually Running?

    If you need to knowThe evidenceNext action
    Whether the post is set up wellStored and served title, description, H1 and alt, side by sideFix the gaps in the order below
    Whether the whole site has the same gapA crawl across every pageRead the technical SEO audit approach, then batch the fix
    Whether a page is reachable at allSitemap and internal links, not on-page fieldsSee the sitemap audit and the orphan pages post
    Whether AI answer engines can use the pageStructured data and clear, quotable textSee AI search optimization guidance

    Fixing Findings In The Right Order

    OrderFixWhy here
    1Take a restore pointEverything after this changes the post
    2Resolve the H1 questionA human decision, and it changes what Google may treat as the main title
    3TitleHighest-visibility field, but only one of several inputs
    4DescriptionA pitch Google may or may not use
    5Alt text on meaningful imagesFix real gaps, leave decorative images empty on purpose
    6Internal linksAdded by hand, where they help a reader
    7SchemaLast, and only if none exists yet

    Two rules hold for every row. Take the restore point first. Re-read the live page after each change, because a cache can show the old version for a while.

    A write that you cannot undo is a different kind of risk. Our post on what a rollback actually reverses is a good test of any undo claim. And before an agent gets write access to a site at all, the WordPress MCP ability list shows how to sort what it can do by what you would let it do unattended.

    A Five-Post Audit You Can Do This Week

    Take five published posts, ideally the ones that earn the most traffic. For each, record:

    1. The served title and its length.
    2. The served description and its length, or “missing”.
    3. The H1 count on the live page.
    4. Images without alt, split into meaningful and decorative.
    5. The number of internal links, and whether any point to a post that no longer exists.

    Then pick one outcome per post: fix it now, test it on a staging copy first, hand it to the person who owns the content, leave it as is with a note, or defer it until the plugin question is settled.

    If a post fails three of the five, start there. A fixed list of steps is easier to repeat than a clever one, which is the argument in our piece on writing the update process down before it runs.

    Why On-Page Fields Go Stale

    Plugins get swapped, themes change and titles get rewritten by someone else. A title that was right in March can be wrong in October. A theme update can add a second H1. A plugin swap can leave the description in a field the new plugin does not read, and a cache can keep serving the old page. Nothing in the editor will tell you any of that. Run the five-post audit again after any theme or SEO plugin change.

    Two Outside Views

    A tutorial and a forum thread. Neither is an endorsement.

    Technical Bro’s WordPress SEO course is a Rank Math tutorial. By its own description it covers the SEO title and meta description, heading structure, image alt text, internal linking and schema, which are the six things this checklist reads. The description also says that following a checklist does not guarantee first-page rankings.

    A WordPress SEO tutorial built on Rank Math, covering title, meta description, headings, alt text, internal links and schema.

    Technical Bro, “Wordpress SEO full course 2026 | How to Rank Website on Google First Page | Rank Math SEO Tutorial” on YouTube, 27 Sep 2026

    On Reddit, an r/Wordpress thread describes a company site running two SEO plugins, Rank Math and All in One SEO, and the poster asks what to check before removing one. That is step 2 of the playbook as a real question: which plugin owns the title and description.

    “…is there anything I need to consider when deactivating one of the plugins?”

    r/Wordpress, “Company Site has 2 SEO Plugins, Need to Remove One.”, October 2026

    Where Protuno Fits

    In the Protuno dashboard, the SEO agent is Iris, and this playbook’s category is SEO. It is a Beta playbook that can change a site, so it needs the Protuno plugin on the site first. It works on one post you name, shows its proposed changes as a diff, snapshots before it writes, and reads everything back.

    It does not choose the post, and it does not replace judgement on headings and links.

    Sources And Further Reading

    The Point

    A score says how a post looked in the editor. A checklist says what the page serves.

    Run the checks in order, change one thing at a time, and keep the old value.

    Which of your five best posts has never had its served title and description read side by side?

  • WordPress Staging Site: What Drifts From Live

    WordPress Staging Site: What Drifts From Live

    The update passed on staging. You pushed it to live. Live broke.

    Nobody did anything wrong in the update itself. The test was honest. It was just run against a site that had quietly stopped being a copy of the one that matters.

    That gap has a name worth using with clients: drift. A WordPress staging site and its live twin start identical on the day the clone is made, and then they separate, one small change at a time, until the test tells you about a site that no longer exists.

    Short answer: a staging site is only as useful as the distance between it and live, and that distance is measurable. Before you trust a staging test, compare six things: plugin and theme versions, database content, settings rows, the search engine visibility flag, cron, and the caching and CDN layer. If any of them differ in a way that touches the change you are testing, the test proved less than you think.

    What this post is, and is not: this is a comparison routine you can run yourself on two sites you control, from outside and from inside. It is not a claim that a matched staging site makes an update safe, and it is not a review of any specific host’s staging feature. Hosts implement staging differently, so confirm how your own host’s push behaves before you rely on it.

    A side by side comparison of a live WordPress site and its staging copy, with three rows highlighted where the two no longer match.
    The clone was identical on day one. This is day ninety.

    Why A Matching Staging Site Stops Matching

    Nobody decides to let staging drift. It happens because the two sites are used by different people for different reasons.

    Live gets content. Orders, posts, form entries, new users, a plugin the client installed after seeing it recommended somewhere. Staging gets experiments. A theme tweak nobody reverted, a plugin activated to test one thing six weeks ago, a constant added to wp-config.php during a debugging session.

    Both sites are being maintained honestly. They are just being maintained in different directions.

    The failure this produces is specific and it is the worst kind: a false pass. A failed staging test is useful, because you learn something before the client does. A passed staging test on a drifted site teaches you nothing and feels like proof. You then push to live with the confidence of someone who tested, which is exactly the confidence that skips the backup step.

    That is why drift belongs in the same conversation as the update process written out before it runs. A pre-flight that includes “did staging actually match” is a different pre-flight from one that only asks “did staging pass”.

    Why Core Version Drift Is Worse This Month

    WordPress 7.1.3 shipped on 6 October 2026 as a maintenance and security release, with 7 security fixes and 4 bug fixes. The announcement recommends updating immediately.

    Live sites that take a release like that quickly move to a new core version. A staging copy cloned last month does not move with them. It sits on the old core until somebody remembers it exists.

    So the first drift of the season is the boring one: live runs the patched core and staging runs the one before it. Any test you run on staging now is a test of a version your live site has already left. The plugin updates you queue up next are tested against the wrong foundation.

    The Six Things That Drift First

    Across sites, the same six categories separate before anything else does. They are listed here in the order you should check them, which is roughly the order of how often they bite.

    #What driftsHow it usually happensWhat it costs you
    1Plugin and theme versionsLive gets updates, staging does not, or the reverseYou test against code the live site is not running
    2Database contentLive keeps collecting orders, posts, users and form entriesQueries behave differently at a different row count
    3Settings rowsA plugin reconfigured on one site only, usually during debuggingThe feature you tested is configured differently on live
    4Search engine visibility flagThe clone discourages search engines, and a push carries it backLive goes no-index without anyone noticing
    5CronStaging cron is disabled to stop duplicate emailsScheduled work passes on staging because it never ran
    6Caching and CDNStaging has no page cache and no CDN in front of itNothing you measured about speed transfers to live

    Four and six deserve a sentence more than the table gives them. Cron, row five, is the other quiet one, and it gets its own check further down.

    The visibility flag is the one that turns a maintenance task into an SEO incident. Most staging clones are deliberately set to discourage search engines, which is correct for staging. The danger is the direction of travel: if a full database push moves that option row back to live, the live site is now asking not to be indexed, and nothing on the front end looks wrong. What that setting actually does, and what it does not do, is covered in the robots.txt myth. Read that before you decide how worried to be.

    Caching is the one people misread. A staging site usually runs with the page cache off and no CDN pointed at it, which makes it a reasonable place to test functionality and a poor place to test anything about speed or headers. If your test involved a page loading faster, you measured a site with a different delivery layer. The WordPress page cache header lies explains why the header you would check to confirm this is itself not trustworthy on its own.

    One small setting helps with all six, and it is easy to skip. WordPress lets a site declare an environment type: local, development, staging or production. A site that never sets it is treated as production. If staging and live each declare what they are, plugins and hosts have something honest to read, and you have a one-line fact to compare.

    Diagram of the six categories that drift first between a WordPress live site and its staging copy, with the visibility flag and cron rows flagged.
    Two of the six fail silently. Those are the two to check first.

    Comparing The Two Sites From Outside

    Start outside, because it needs no access and it catches the loudest differences in a couple of minutes.

    Request the same path on both hostnames and compare the responses. You are looking for four things:

    1. The HTTP status and the final URL after redirects. A staging site that redirects somewhere unexpected is telling you its site URL rows are wrong.
    2. The X-Robots-Tag response header, and the robots meta tag in the HTML, on both.
    3. Any cache or CDN headers present on live and absent on staging, or the reverse.
    4. The generator tag and any versioned asset URLs, which give you a rough read on whether core and plugin versions line up.

    Then fetch /robots.txt on both and read them side by side rather than skimming.

    curl -sI https://example.com/ | grep -i -E "^(HTTP|location|x-robots-tag|cache-control|age|cf-cache-status|x-cache)"
    curl -sI https://staging.example.com/ | grep -i -E "^(HTTP|location|x-robots-tag|cache-control|age|cf-cache-status|x-cache)"

    This outside pass will not tell you about settings rows or database size. It will tell you within minutes whether the two sites disagree about what they are, which is the category of drift most likely to cause a visible incident.

    Comparing The Two Sites From Inside

    The inside pass is where the real distance shows up. On both sites, record the same short list, then diff it:

    • WordPress core version.
    • Every installed plugin, its version, and whether it is active. Inactive but installed counts.
    • Every installed theme and which one is active.
    • The values of blog_public, home, and siteurl.
    • The row count of your largest content tables, and the total number of rows in the options table.
    • Whether DISABLE_WP_CRON is defined, and when the next scheduled event is due.
    • The environment type each site declares, and any constants in wp-config.php that exist on one site and not the other.

    Two of these are easy to get wrong. Inactive plugins matter because a push can activate them and because they still receive updates. And the options table is worth counting rather than eyeballing: the autoloaded portion of it behaves differently at different sizes, and the autoload query everyone is running covers how to measure it properly.

    Cron deserves its own check rather than a line in a list. A staging site with cron disabled will pass a test of anything scheduled, because nothing scheduled ran. You did not test the feature. You tested that the feature did not crash while sitting still. The failure that never alerts you is the same failure mode on live, which is a useful reminder that this is not a staging-only problem.

    One more inside check is worth the minute it takes: the PHP version. Staging and live on the same host usually match, but not always, and a staging environment left on an older branch will accept code that live rejects outright. The WordPress PHP version problem is the longer argument for treating this as a scheduled concern rather than an occasional one.

    What A Push Overwrites

    Before you push staging to live, the question to answer is not “is the code ready”. It is “what on live is about to be replaced”.

    A files push, a database push and a combined push each replace something different. The database push is the one that ends careers, because live kept collecting real work while staging sat still, and a full database push replaces that work instead of merging it. It also carries the options table, which is how a staging no-index flag reaches a production site.

    Two rules make this survivable. Take a restore point immediately before the push and confirm it is real, which is the whole argument of verifying a WordPress backup. And know in advance what a rollback can and cannot reverse, as set out in what an automatic rollback can undo and what it destroys.

    After the push, re-check the same six categories against live. Re-check the visibility flag and cron first, because those are the two that fail silently.

    A 2026 guide to creating a WordPress staging site, in about three minutes.

    Robbie’s Tutorials, “How to Create a WordPress Staging Site [2026 Full Guide]” on YouTube, 25 Jun 2026

    Has a migration or update ever gone wrong on a live site? What happened?

    r/Wordpress, “How do you handle moving WordPress sites between local/staging/production?”, August 2026

    Both are about getting a staging copy right. The video covers setting one up. The thread is a working question about moving sites between local, staging and production, and the line quoted above is the one this post answers: what goes wrong, and how you would have known.

    What Comparing Staging To Live Does Not Solve

    A matched staging site is a better test. It is not a safe one.

    Even with zero drift across all six categories, a staging test cannot tell you whether:

    • the update behaves the same under real traffic, because staging has none;
    • a third party integration will respond, since most are pointed at test credentials or nothing at all;
    • an email actually sends, because staging usually has outbound mail suppressed on purpose;
    • a payment path completes, for the same reason;
    • a scheduled job runs correctly over days rather than in a single manual trigger;
    • the CDN in front of live will serve the new asset, since staging is not behind it; or
    • the change interacts badly with data volumes that only exist on live.

    None of that is an argument against staging. It is an argument against reading a staging pass as a guarantee. The honest sentence to put in a client report is the same one that belongs in any check: what was tested, on what, and what was not assessed.

    There is also a drift you cannot close by matching. A staging site matched to live on Monday has drifted again by Friday, because live kept working. A comparison is a snapshot, so the useful question is not whether staging matched, but when you last checked that it did.

    Sources And Further Reading

    Which Of Your Staging Sites Is Still A Copy?

    If you run twenty sites, you probably have twenty staging environments, and you almost certainly do not know which ones still resemble the sites they were cloned from.

    Pick five. Run the outside pass, then the inside pass. Record the drift age, the version differences, the visibility flag, and the cron state for each pair.

    So: on the last update you pushed from staging, could you have said, with evidence, how far that staging site had drifted from live before you trusted the test?

  • WordPress User Roles: Who Really Has The Keys

    WordPress User Roles: Who Really Has The Keys

    Every WordPress site has an account nobody remembers creating.

    It might belong to a freelancer from two years ago, a plugin vendor’s support login, or a colleague who has since left. It still works. It is still an Administrator.

    The password is only half the problem. The bigger question is how many people can change your site today, and whether you could name every one of them.

    Short answer: To audit WordPress user roles, open the Users screen, filter by role, and count the Administrators. Each one can install plugins, edit code and create more users. Any Administrator with no named owner and no clear reason to exist is the first account to question. Then check whether anyone has a role above their job, and whether any login is shared.

    What this post is, and is not: A read-only audit of WordPress user roles, for people who look after several WordPress sites. The role descriptions follow how WordPress core documents its default roles. Nothing here is a result from a real client site, and it does not replace a full security audit.

    An Administrator is not a job title. It is a list of everything someone can break.

    Illustration of the five default WordPress user roles as a ladder, from Subscriber at the bottom to Administrator at the top
    The higher the role, the more a single stolen password can change.

    WordPress User Roles: What Each Default Role Can Do

    A single WordPress site ships with five roles. They are ordered by how much damage one stolen login can do.

    RoleWhat it can doWhat it should be used for
    AdministratorEverything on the site: install and delete plugins and themes, add and remove users, change any setting, and edit theme and plugin files unless file editing is switched offThe few people who run the site
    EditorPublish and edit all posts and pages, including other people’s, and moderate comments. No plugin, theme or user controlContent managers
    AuthorPublish and edit their own posts, and upload filesRegular writers
    ContributorWrite and edit their own posts, but not publish them or upload filesGuest writers
    SubscriberRead, and edit their own profileMembers, customers, commenters

    For a short overview of the defaults, there is a recent video, WordPress User Roles Explained (2026), from VIS Mountain Marketing & Advertising. Its description says it is narrated with an AI voice and shows simplified diagrams, not live screenshots, so use it as a refresher and check the official documentation for detail.

    What each WordPress role can really do, which one to give each kind of person, and how to change someone’s role in under a minute.

    VIS Mountain Marketing & Advertising, “WordPress User Roles Explained (2026): Which Role to Give Each Person + How to Change It” on YouTube, 6 Oct 2026

    Two things change this picture.

    First, plugins add roles and change what the default ones can do. A store plugin adds a Shop Manager. A membership plugin adds its own. These roles can carry real power, and they rarely stand out in a quick look at the Users screen.

    Second, a multisite network has a Super Administrator above every site. That one account controls the whole network, not a single site.

    Of all the WordPress user roles, one matters most for an audit. Administrator decides who can change everything else.

    Listing Every Account From Outside And Inside

    You can see your accounts and their WordPress user roles from two places, and the two lists do not match.

    From inside, the Users screen or the command line shows every account, its role and when it registered. From outside, you only see what the site chooses to reveal. By default, a WordPress site can reveal usernames through public routes. We covered all three of them, and how to close them, in WordPress user enumeration is still on by default, so this post does not repeat that.

    What matters here is the gap between the two lists.

    ViewWhat it tells youWhat it cannot prove alone
    Inside, Users screenEvery account and its roleWhether anyone still uses it
    Inside, command lineThe same list, in a form you can save and compare next monthWho the person behind a login really is
    Outside, public routesWhich usernames a stranger can findWhich of them are Administrators
    Illustration comparing the account list seen from inside WordPress with the shorter list visible from outside, with the gap highlighted
    An account that only shows up on one side is a question worth asking.

    A small audit you can run today:

    • Count the accounts in each role on the Users screen.
    • Save the Administrator list with name, email and registered date.
    • Write an owner and a reason beside every Administrator.
    • Mark every account you cannot explain.

    With command-line access, these two commands cover the first steps:

    wp role list
    wp user list --role=administrator --fields=ID,user_login,user_email,user_registered

    The registered date is the cheapest clue you have. An old account with no known owner is the first one to question.

    One more caution. Code that runs inside WordPress can change what WordPress shows you. The r/Wordpress write-up linked below describes malware that kept a list of administrator accounts to hide, so a Users screen can look clean when it is not. If you suspect a problem, ask the database directly. This query lists every account with the Administrator role, and you should change each wp_ to your own table prefix:

    wp db query "SELECT u.ID, u.user_login, u.user_email, u.user_registered FROM wp_users u JOIN wp_usermeta m ON m.user_id = u.ID WHERE m.meta_key = 'wp_capabilities' AND m.meta_value LIKE '%administrator%'"

    If that list is longer than the Users screen, treat it as an incident and not a tidy-up.

    A 34-second walk-through of the audit. All scenes are illustrations with example accounts.

    The Three Accounts To Question First

    An audit of WordPress user roles does not need to be long. Three kinds of account cover most of the risk.

    1. Administrators nobody can name. If no one on your team can say who it belongs to and why it exists, treat it as a finding. A forgotten Administrator is an unlocked door that nobody knows about.

    2. Accounts with a role above their job. A writer who publishes their own posts needs Author, not Editor. A client who updates one page needs Editor at most. Ask whether the person could do their job one role lower. Agencies argue about whether a client should get Administrator all the time, and the least-privilege answer is almost always no.

    3. Shared logins. One account used by three people cannot be traced to any of them. When something changes, nobody can say who did it. Give each person their own account, then remove the shared one.

    One r/Wordpress thread, “Found a WordPress malware using __GA_INJ_START__ and hidden admin accounts — full incident analysis”, is a write-up of a hacked site where the attacker kept hidden administrator accounts. The author describes how the malicious code worked:

    “The malicious code maintained a list of administrator accounts that should be hidden.”

    r/Wordpress, “Found a WordPress malware using __GA_INJ_START__ and hidden admin accounts — full incident analysis”, August 2026

    What Changing A Role Breaks

    Removing access is easy to say and harder to do. A role change can stop things that quietly depend on it.

    Check these before you demote an account:

    • Scheduled work. Does a plugin run jobs as that user?
    • Authorship. Posts belong to the account. Deleting the user forces a choice about where the content goes, while demoting does not.
    • Integrations. An automation tool or API may sign in with that login.
    • Backups and updates. Some tools act as a named Administrator.

    The safe order is short. Demote first, and do not delete. Watch the site for a few days. Delete only when nothing has broken.

    Take a restore point before changing roles on a real site. If something does break, a rollback is only as good as what it can actually undo, so find that out before you need it.

    Checking It On A Schedule

    A WordPress user roles audit done once goes stale. People join, leave and change jobs, and plugins add roles.

    Keep the software current as well. WordPress 7.1.3 was released on 6 October 2026 as a maintenance and security release, and the announcement recommends updating immediately. A tidy account list says nothing about the code that guards it.

    A monthly pass is enough for most sites. Compare this month’s Administrator list with last month’s. Anything new needs an owner and a reason. Anything missing should be a change you expected.

    For agencies, this belongs in the client report as one short line: how many Administrators, and whether each one is accounted for. We wrote about what goes into a client report and what stays out. An Administrator list is a good example of a short finding that earns its place.

    A role review also fits inside a wider WordPress security audit, next to the basics in WordPress hardening.

    What This Does Not Solve

    An audit of WordPress user roles is a view of access, and it has limits.

    • It does not tell you a password is strong. A well-named account with a weak password is still a weak account.
    • It does not show activity. Core WordPress does not record who logged in or what they changed. You need an activity log for that.
    • It does not see plugin roles in full. Some plugins grant power through their own settings, not through the role name.
    • It does not see an account that hides itself. The Users screen is only as honest as the code running behind it. Compare it with a direct database query when something feels off.
    • It does not catch an account that was created and removed. If someone added an Administrator, did their work and deleted it, the list looks clean.
    • It does not cover access outside WordPress. Hosting panels, SFTP, database users and DNS each have their own accounts.
    • It does not cover access given to software. Connecting an AI tool or an automation is another kind of access and needs its own questions. We listed them in AI agent permissions on WordPress.

    A clean list from a check that skipped half these places is worse than an honest “I reviewed WordPress users only.” Say which places you covered.

    A Five-Site Audit You Can Do This Week

    Pick five sites you look after. For each one, write down:

    • The count of accounts in each role.
    • Every Administrator, with a name, an owner and a reason.
    • Any account that has not logged in for a long time, if you can tell.
    • Any account with a role above the person’s job.
    • Any shared login.

    Then choose one outcome for every account you questioned:

    1. Keep it, with an owner and a review date.
    2. Demote it one role.
    3. Test the change on a copy first, then demote.
    4. Replace a shared login with named accounts.
    5. Remove it, after a demotion period with nothing broken.
    6. Investigate it, if nobody can explain it.

    Investigate first. An Administrator nobody can explain decides whether this was a tidy-up or an incident.

    Sources And Further Reading

    The Point

    WordPress user roles are a list of who can change your site. Most sites hold more of that power than anyone has written down.

    You do not need a tool to start. You need an hour and a list.

    How many Administrators does your busiest client site have, and could you name every one of them?

  • WordPress Hacked: The First Hour, In Order

    WordPress Hacked: The First Hour, In Order

    The worst moment of a hacked site is not the discovery. It is the next ten minutes.

    You open the dashboard, you start deleting things, you reset one password, you restore a backup. Every one of those moves feels like progress. Several of them destroy the evidence you need, or lock the door while the intruder is still inside.

    Short answer: When a WordPress hacked site lands on your desk, do the first hour in this order. Write down what you see. Copy the site as it is. Cut off access, including the access that sits above WordPress. Check the backup before you trust it. Clean, update, and rotate passwords again once the site is clean. Only then ask how they got in. The order matters more than any single tool.

    What this post is, and is not: A practical response checklist for people who run or look after WordPress sites. It draws on the official WordPress.org guide to a hacked site. The order of the seven moves, and the reasons for it, are ours. It is not a malware removal service, not legal advice, and not a result from a real incident. If the site holds customer payment data or personal data, tell your host and take professional help early.

    A scan tells you what is wrong. A response tells you what to do first.

    Illustration of the seven steps in the first hour after a WordPress site is hacked: record, copy, lock access, check the backup, clean, rotate passwords again and find the cause
    Seven moves, in this order. Skipping ahead is how sites get hacked twice.

    Why Panic Cleaning Makes It Worse

    The urge is to fix what you can see. A strange admin user, a defaced page, a spam redirect. You remove it and the symptom goes away.

    That tells you very little. A visible symptom is often the last thing an intruder did, not the first. The way in is usually still open, and a copy of the way back in may already be sitting in a file you have not looked at.

    The WordPress.org guide opens with a plain instruction: stay calm, then document. It says a hack is an ambiguous word that tells you little about what happened, so the symptoms you can name are the thing to write down first.

    Panic cleaning has three common results:

    • Evidence is lost. If you delete before you copy, you cannot tell afterwards what was changed.
    • The door stays open. Cleaning files without changing access lets the same account or key back in.
    • A bad backup gets restored. A copy taken after the break-in can bring the problem straight back.

    We looked at the scanning side in WordPress Malware Scan. A scan answers “what is on this site”. This post answers the question that comes before and after it: what do I do, and in what order.

    Seven Moves For A WordPress Hacked Site

    #MoveWhy it comes here
    1Write down what you seeIt is the only record of what the site looked like before you touched it
    2Copy the site as it isYou need the evidence, and a safety net if the cleanup goes wrong
    3Lock accessStops new changes while you work
    4Check the backupA restore is only safe if the copy is older than the break-in
    5CleanRemove the cause, not just the symptom
    6Update and rotate againFixes the known weakness and replaces anything the intruder saw
    7Find how they got inWithout it, the same hole is still there

    Each move gets its own section below.

    Move One: Write It Down Before You Touch Anything

    Open a note and write four things. It takes five minutes and it is the most useful five minutes of the hour.

    • What you are seeing that tells you the site is hacked.
    • When you noticed it, with the time zone.
    • What changed recently: a new plugin, a theme edit, a new user, a host move.
    • Where the site is hosted, and who else has access.

    The WordPress.org guide lists the kind of symptoms that count as clear signs: the site is blocked by a search engine, the host has disabled it, it is flagged for distributing malware, visitors report their antivirus is warning them, you hear it is being used to attack other sites, or you see actions nobody authorised, such as new users.

    Write down which of those you actually saw. “It looks hacked” is not a symptom. “A new administrator account appeared on Tuesday” is.

    Move Two: Copy The Site As It Is

    Before you clean, take a full copy of the files and the database. Keep it somewhere that is not the same server.

    This copy is not a backup you plan to restore. It is evidence. If the cleanup breaks something, you can go back. If you later need to work out how the intruder got in, you have something to read.

    The same WordPress.org guide makes the point that even an infected copy is worth keeping. It recommends one more snapshot of the environment before the cleaning starts.

    Ask your host for logs while you are at it. Many hosts keep very little for very long, so ask early. We cannot say what yours keeps. The point is that a log you did not ask for in time is a log you will not have.

    Move Three: Lock Access, Including The Part Above WordPress

    Locking access means more than changing the admin password.

    Think of access in three layers:

    LayerExamplesOften missed because
    WordPressAdmin and editor accounts, application passwordsIt is the layer everyone remembers
    HostingControl panel, SFTP, SSH keys, database userIt sits above WordPress and plugins cannot see it
    PeopleLaptops, saved FTP passwords, shared loginsThe weak point is a machine, not the site

    For WordPress itself, the official guide suggests forcing a password reset for all users, especially administrators, and changing the secret keys in wp-config.php. Changing the keys logs everyone out, including a session the intruder may still be holding.

    For hosting, change the SFTP, control panel and database passwords, and remove any key or user you do not recognise. If you cannot name an account, treat it as a finding.

    For people, the guide recommends scanning your own computer too. In many cases the infection starts on a local machine that captured a login.

    If you cannot log in at all, the guide describes a way back through the database tools your host provides, by resetting the user record directly. That is a task for someone comfortable in the database.

    We looked at how accounts and routes get exposed in WordPress user enumeration. It is a useful read for the question “which names can an outsider see”.

    Move Four: Check The Backup Before You Trust It

    Restoring a backup feels like the safest move. It is only safe if the backup is older than the break-in.

    An intruder may have been inside for days before anything visible happened. A backup taken last night can contain exactly what you are trying to remove. The WordPress.org guide notes that you can restore a backup and move straight into the investigation, which is the careful way to read it: restore to look, not to relax.

    Three questions to ask of every backup:

    • When was it taken, compared with the first sign of trouble? Use your notes from move one.
    • Where is it stored? A backup on the same server may have been reached too.
    • Has it ever been restored on a test copy? If not, it is a file with a hopeful name. We wrote about that in verifying a WordPress backup.
    Illustration of a timeline showing that a backup taken before the first sign of the break-in is safer to restore than one taken after it
    A backup is only as clean as the day it was taken.

    Move Five: Clean The Cause, Not The Symptom

    Cleaning means replacing, not patching. The guide’s approach is practical and worth following closely.

    • Core. The folders wp-admin and wp-includes can be replaced safely with a fresh copy of the same WordPress version. The guide suggests using SFTP to upload them rather than the dashboard reinstall, because the dashboard installer tends to overwrite existing files and intruders add new ones.
    • Plugins and themes. Reinstall them from their original source. A modified file that looks normal is the whole point of a backdoor.
    • The .htaccess file. The guide calls it one of the most commonly modified files, whatever the type of infection, and notes it may sit in more than one folder.
    • Files that load on every request. index.php, header.php, footer.php and functions.php are named in the guide because a change there affects every page.
    • Users and the database. Check administrator accounts and look for anything you did not create.

    Deactivating is not removing. A file that runs when someone requests its address does not care whether the plugin around it is switched on. Delete what should not be there.

    The official guide calls finding and removing the hack the most daunting part of the whole process, and it gives no single method, because the right steps depend on the symptoms and on your own technical skill. If you are not sure, a professional clean is cheaper than a second hack.

    One practical walk-through is Ferdy Korpershoek’s video, WordPress Getting Hacked? Here’s What To Do Now, which shows a real hacked website being scanned and cleaned. The description says it uses the MalCare plugin and discloses an affiliate link, so read it as one practitioner’s method, not as a recommendation from us.

    I’ll even show you a REAL hacked website, how malware was detected, and how we removed it in just a few clicks.

    Ferdy Korpershoek, “WordPress Getting Hacked? Here’s What To Do Now” on YouTube, 18 May 2026

    Move Six: Update, Then Change The Passwords Again

    Once the site is clean, update WordPress, plugins and themes to the latest versions. The guide says older versions are more prone to being hacked, and that is where many incidents start.

    A timely example. WordPress 7.1.3 was released on 6 October 2026 as a maintenance and security release. The announcement lists seven security fixes and four bug fixes, and says that because it is a security release, updating immediately is recommended. We cannot say whether any particular hack used any of those issues, and this post does not claim that. The practical point is smaller: if your site is behind on core updates, that is the first thing to fix.

    Then change the passwords a second time. The guide is explicit about this. If you changed them when you found the hack and the intruder was still inside, the new passwords may have been seen. Change them again after the site is clean, and consider changing the database user and password as well.

    Two things are worth turning on at this point: a second factor on logins, and a short list of who has access at all. We looked at what happens when a vulnerable plugin goes unnoticed in WordPress vulnerability scanner design and at forgotten plugins in removed plugins.

    A 34-second walk-through of the seven moves. All scenes are illustrations, not a real incident.

    Move Seven: Find Out How They Got In

    This is the step that gets skipped, because the site looks fine again.

    The WordPress.org guide calls it forensics: understanding what happened and how the attacker got in, so it cannot be used again. It admits that is hard for most site owners because of missing data and technical depth.

    You can still narrow it down with what you already wrote and copied:

    • Compare the date you first saw a problem with the dates of your last plugin and theme changes.
    • Look at the accounts that were added, and when.
    • Check whether any plugin or theme was out of date, abandoned or removed from the repository.
    • Ask whether the PHP version was supported. See the WordPress PHP version problem.
    • Ask the host what they saw on their side.

    If you cannot find the cause, say so in your notes. “Unknown, access rotated, monitoring added” is an honest record. A made-up cause is worse than none.

    A site that comes back dirty is exactly what this step is for. One r/Wordpress thread, “How to fix wordpress hacked website? Can anyone guide step by step.”, starts from a site owner whose developer cleaned the site and saw the files return. Without knowing how the intruder got in, a clean-up can only hope the same door stays shut.

    “I got that checked by developee and he cleaned it but everything came back.”

    r/Wordpress, “How to fix wordpress hacked website? Can anyone guide step by step.”, October 2026

    Blacklists And Search Console Warnings

    Search engines and antivirus tools may flag a hacked site. The guide recommends registering the site with the webmaster consoles so you hear about it from them. Google Search Console also has a Manual actions report. If Google has issued a manual action against the site, you fix every affected page and then select Request Review in that report, describing what you fixed. Check the report for the exact wording that applies to your case.

    Treat that as a signal, not as proof. Not seeing a warning does not mean the site is clean, and seeing one does not tell you what is wrong. Check the site, then check the report.

    What This Does Not Solve

    A first-hour order is a way to stop making it worse. It does not make a hack impossible.

    • It does not tell you how you were hacked. Move seven narrows it down. It does not prove it.
    • It does not guarantee a clean site. Intruders can plant more than one way back in. A single clean scan is not the end.
    • It does not replace professional help. Customer data, payment data and legal duties need someone qualified.
    • It does not prevent the next one. Prevention is a different job. We measured common hardening advice in WordPress hardening.
    • It is not a result from a real incident. Nothing in this post comes from a site we cleaned.

    A response checklist is for the day it happens. A monitoring habit is for the days before it. Our write-up of WordPress security monitoring covers the second one.

    A Five-Site Check You Can Do This Week

    You do not need a hack to practise. Pick five sites you look after and answer these for each:

    1. Where would you write down what you see, and who gets the note?
    2. Can you take a full copy of files and database in under an hour?
    3. Who holds hosting, SFTP and database access, and when were those passwords last changed?
    4. Has your newest backup ever been restored on a test copy?
    5. Is the site on the latest WordPress version, with a supported PHP version?

    Then pick one outcome per site: fix it now, test it first, hand it to someone who owns it, or accept the gap with an owner and a date.

    If a site fails three of the five, start with that one. Our piece on the client report is a useful reminder of how to tell a client what you found, in the order they need it.

    Sources And Further Reading

    The Point

    A hacked site is two jobs: stop it, then understand it.

    The first hour is about doing them in that order.

    Which of your sites has no written answer to “who has access, and when did we last change it”?

  • WordPress Spam Comments: The Playbook, Step By Step

    WordPress Spam Comments: The Playbook, Step By Step

    Most spam cleanup is a button labelled Empty Spam. The button never asks which of those comments were real.

    A spam folder is a pile of guesses. Most are right. A few are a customer asking a question, and once the folder is emptied nobody can tell which few.

    Short answer: safe WordPress spam comment cleanup needs a second signal beyond the spam label, a rule that stops the whole job when the numbers look wrong, and a way back. The Protuno Spam Comment Deleter playbook does this in four steps. It moves only the comments that carry two signals to the trash, it never permanently removes anything, and it checks afterwards that the comments you wanted to keep did not move.

    What this post is, and is not: it is a practical walk through one playbook and the reasoning behind its order. It describes how the check works from the playbook’s own step files. It does not quote a run, a count or a result, because this post is written without running the playbook.

    An empty folder is a fact about a button. A clean comment section is a fact about every comment you kept.

    Four steps of the Spam Comment Deleter playbook, from baseline and stop rule to a verified report

    Why Emptying The Spam Folder Falls Short

    WordPress gives every comment one status: approved, held for moderation, spam or trash. That status is a single label, set by whatever last touched the comment. A spam filter set it. A person set it. A plugin that misfired set it.

    Emptying the folder trusts that label completely. If the label is wrong on one comment, the comment is gone. There is no second look, because the button was built for speed.

    The opposite habit is just as common. The folder is never emptied, so spam that was caught and never cleared sits in the database indefinitely, and the comment tables grow for years. Nobody wants to be the person who clicks the button, so nobody does.

    Both habits come from the same gap. There is no step between “flagged” and “gone” that checks the flag.

    That gap is the interesting part of this playbook. It is also why the job is a poor fit for a blind schedule and a good fit for a written sequence.

    Three Signals On One Comment

    The playbook decides what is safe to move by looking at each spam comment in three ways. Any one of them alone is weak.

    SignalWhat it tells youWhat it cannot prove alone
    Comment status is spamSomething classed it as spamThat the classifier was right
    Akismet result on the commentA detector independently said true (or 1)That the comment has been there long enough to be settled
    Age of the commentIt is older than a minimum (10 minutes by default)That it is spam at all

    The rule that follows from the table is short. A comment is eligible only when it is already spam, a detector has confirmed it, and it is not brand new. The playbook calls that the dual-signal rule.

    Two cases matter more than the rest. An Akismet result that is explicitly false means the detector said this is not spam, so the comment is never eligible. A comment with no Akismet data at all has no second signal, so it is not eligible either, and the playbook will not call it confirmed spam.

    Three signals compared: spam status, Akismet confirmation and age, with the combinations that qualify

    A Small Test You Can Run Today

    You can see the shape of your own problem in about ten minutes, with nothing installed.

    Write down five things for one site:

    • How many comments are approved, held for moderation, in spam and in trash.
    • Whether Akismet is installed and active.
    • Whether the trash is switched on, meaning a retention period is set rather than zero.
    • The oldest comment in the spam folder.
    • Any spam comment you recognise as a real person.

    Here are the places to look:

    /wp-admin/edit-comments.php?comment_status=spam
    /wp-admin/edit-comments.php?comment_status=trash
    /wp-admin/plugins.php          (is Akismet listed and active?)
    wp-config.php                  (search for EMPTY_TRASH_DAYS)

    If you find even one real comment in the spam folder, you have your reason to read on. A cleanup that cannot tell that comment apart is a cleanup you should not schedule.

    The Playbook, Step By Step

    Spam Comment Deleter is a Protuno Care playbook. It runs a fixed sequence of four steps, and only one of them changes the site. In the dashboard it carries the CAN WRITE label. The public playbooks page lists the same four-step playbook as Comment Cleanup, described as clearing the spam that WordPress has already flagged, and nothing else. The dashboard card calls it Spam Comment Deleter, so you may see either name.

    #StepWhat it does
    1Comment Baseline And Anomaly GateReads the comment counts (approved, moderated, spam, trash), works out the spam ratio, applies the stop rule, and records the trash retention period and whether Akismet is installed. Changes nothing.
    2Build High Confidence Spam BucketSorts spam comments into buckets under the dual-signal rule: eligible, medium (spam only, never actioned) and too new (skipped). Changes nothing.
    3Trash Confirmed SpamThe only step that writes. Moves eligible comments to the trash one at a time, after a retention check and a written audit manifest.
    4Verify Spam Cleanup ReportRe-reads the counts, asserts that spam dropped by exactly the number trashed and that approved and moderated did not change, then writes the report. Changes nothing.

    The order is the safety. The counts and the stop rule come before any bucket exists. The bucket exists before anything is trashed. The check comes after, and it is the only one that can say the earlier steps did what they claimed.

    Step 1 and step 2 both use one read capability, called comments-spam-audit, to compute the counts and the buckets on the site itself. The step file explains why: an earlier version paged the comments over the REST API, which cannot see comment meta. That version got seven of nine counts wrong and planned to trash six spam comments, including one whose Akismet result was explicitly false. The fix was to stop rebuilding the numbers from REST pages.

    That is a good rule for any cleanup tool you are considering. The classification has to be read from where the signal lives.

    What The Stop Rule Is Protecting You From

    Step 1 computes one ratio: spam divided by approved plus moderated plus spam. Trash is left out on purpose, because trashed comments are spam that has already been dealt with. The default threshold is 0.6.

    Here is how that works, with made-up numbers to show the arithmetic and not a result from any site.

    IllustrationApprovedModeratedSpamRatioGate
    A normal blog30020800.20Proceed
    A broken classifier10009000.90Stop

    When the gate says stop, the playbook stops the whole job. It reports that the spam volume is anomalous, likely a misconfigured classifier, and that manual review is needed before any cleanup.

    The reasoning is sound. A huge spam share is more likely to be a classifier gone wrong than a site that is 90 percent spam. Mass-trashing it could bury the real comments. A stop here counts as a correct outcome, not a failure.

    Step 3 adds its own gate. If the trash retention period is zero or unset, moving a comment to the trash would remove it for good. The playbook refuses and says so. By default WordPress keeps trashed items for 30 days, and the playbook reads the actual value on your site rather than assuming it.

    What Spam Comment Cleanup Does Not Solve

    • It does not stop new spam. It clears comments that are already flagged. Preventing them is a different job.
    • It does not work without a detector. If Akismet is not installed, nothing can be confirmed under the dual-signal rule, so nothing is eligible. The report leads with that root cause and recommends installing and activating Akismet.
    • It does not touch the medium bucket. Spam without a second signal is reported, and the recommendation is to review it by hand in the Comments screen.
    • It does not remove anything permanently. The playbook trashes. It never deletes and never recommends emptying the trash.
    • It does not touch approved or moderated comments. Step 3 re-checks every comment right before moving it and skips anything that is not still spam.
    • It does not read intent. A comment confirmed as spam is spam by two signals. That is strong evidence, not a mind reader.

    Questions To Ask Your Current Tooling

    • Does it use a second signal before it removes anything, or only the spam label?
    • What happens to a comment with no detector data?
    • Does it stop on a strange spam ratio, or does it carry on?
    • Does it check that your trash is switched on before it trashes?
    • Does it write down what it is about to move, before it moves it?
    • Can a whole run be undone with one action?
    • After the run, does anything compare the real comments before and after?

    If the answer to most of these is “I am not sure”, the tool is a button with a schedule.

    Which Check Are You Actually Running?

    If you need to knowThe evidenceNext action
    How much spam is sitting thereCounts of approved, moderated, spam and trashBaseline step, read-only
    Whether the classifier is healthySpam ratio against a thresholdStop and review by hand if it is anomalous
    Which spam is certainSpam status plus Akismet confirmation plus ageMove that bucket to the trash
    Which spam is only probableSpam status without a second signalReview by hand in the Comments screen
    Whether the cleanup was exactSpam dropped by the trashed count, real comments unchangedRead the verification report

    Fixing Findings In The Right Order

    When a run produces buckets, the order of work matters.

    PriorityFindingWhat to do
    1The stop rule firedFind out why the spam ratio is so high before touching anything
    2Trash is disabledSwitch the retention period on first, then clean up
    3No detector installedInstall and activate Akismet, then run again
    4High-confidence bucket has entriesMove them to the trash and read the report
    5Medium bucket has entriesReview by hand, comment by comment

    Two practical rules sit under this table.

    Restore point first. Before anything that changes a site, know how you would get back. The playbook writes an audit manifest of the exact comments it plans to move, before it moves any, and a run report that can roll the run back. For the site as a whole, verify the backup as well, because a backup you have never checked is a file with a hopeful name.

    Re-check after the change. The final step exists because “it said it worked” is not evidence. Step 4 compares the before and after counts.

    A Five-Site Audit You Can Do This Week

    Pick five sites. For each one, write down:

    • Approved, moderated, spam and trash counts.
    • Whether Akismet is active.
    • Whether the trash retention period is set.
    • The age of the oldest spam comment.
    • Whether any real person is sitting in the spam folder.

    Then decide one of these for each site:

    • Fix now: the folder holds certain spam and the trash is on.
    • Test first: the ratio looks odd, so find the cause before cleaning.
    • Replace: the site has no detector, so install one.
    • Remove: a comment form nobody uses and nobody moderates.
    • Accept with an owner and a date: the spam can stay, with a named person and a review date.
    • Investigate: the spam folder contains something that looks like a real person.

    It is the same arithmetic as the rest of maintenance work. Every small chore multiplies by the number of sites, which is where the unbilled half hour comes from.

    Why Spam Cleanup Goes Stale

    A cleaned folder is a snapshot, and new spam arrives the next day. Security Monitoring has a section on why a one-off security check goes stale, and the same logic applies here. The playbook is listed as weekly on the public playbooks page for that reason.

    Where Protuno Fits

    Spam Comment Deleter sits in the Care category. It is one of the playbooks that can change a site, so it needs the Protuno plugin on the site, and only one of its four steps writes. The other three only read. The decision to run it, and what to do with the comments it leaves alone, stays with a person.

    The split between reading and acting is the point of detection being solved while deciding is not. If you want the update version of this approach, The WordPress Update Process, Written Out Before It Runs is the closest match. And before you connect any automation to a client site, read what to ask about permissions.

    If the question is how a rollback works in practice, WordPress Rollback After An Update covers it, and it names spam comments among the changes the run report can undo.

    The Point

    A spam filter decides. A cleanup should check.

    The safe version needs two signals, a stop rule, a trash that is switched on and a before and after count.

    Which of your sites would you trust to empty the spam folder tonight?

  • WordPress Maintenance Plans: What The Contract Should Name

    WordPress Maintenance Plans: What The Contract Should Name

    Many maintenance plan arguments are not about price. They start with one sentence.

    “Can you just fix this?”

    The client believes the plan covers it. You believe it does not. Neither of you wrote down which one is true.

    Short answer: A maintenance plan contract should name six things in plain words: the work list and how often each task runs, what counts as extra, how fast you respond and how fast you fix, who owns every account, what you do not promise, and how the plan ends. If a line is missing, the first disagreement will fill it in for you.

    What this post is, and is not: A practical checklist for agencies and freelancers who sell or buy WordPress maintenance. It follows the problem from the contract side, not the price side. It is not legal advice, and a lawyer should review any contract before you use it. Nothing here is a result from a real client site.

    A plan is a promise. The contract is where the promise gets a size.

    Illustration listing the six things a WordPress maintenance contract should name: work list, extras, response times, access, exclusions and exit
    Six lines, written once, save most of the arguments later.

    Why A Plan Without A Contract Becomes An Argument

    A plan often starts with a list of friendly words. Updates, backups, monitoring, support. Each word means something different to the person selling it and the person paying for it.

    “Support” is the worst one. To a client it can mean anything that goes wrong. To an agency it means answering a question. The gap between those two meanings is where unpaid hours live.

    We covered the money side of that gap in The Half Hour Nobody Bills. This post covers the paper side. If the paper is vague, no price is safe.

    Six Lines The Contract Should Name

    LineWhat to writeWhat goes wrong when it is blank
    Work listEach task, how often it runs, and who does itThe client assumes more is done than is done
    ExtrasWhat costs more and how it is quotedEvery request becomes a debate
    Response timesWhen you reply, and when you aim to fixA client waits a day and decides you ignored them
    AccessWho owns hosting, domain, licences and loginsA handover turns into a hunt
    ExclusionsWhat the plan does not promiseA normal failure looks like a broken promise
    ExitNotice period and what the client gets backCancelling becomes a conflict

    Each line gets its own section below.

    The Same Promise, Vague And Named

    The fastest way to see the difference is to write one promise twice.

    VagueNamed
    Keep plugins up to dateApply plugin updates weekly, on a copy first, and list what changed in the monthly note
    Daily backupsA backup every day, stored off the server, kept for a stated period, with a restore test on a stated schedule
    Fast supportA reply within the stated hours, and a separate fix target that depends on the cause
    Security includedNamed checks run on a schedule, with the results shared. No promise that a site can never be breached
    Small changes includedText and image swaps on existing pages are covered. New pages and features are quoted

    The named version is longer. It is also the only one a client and an agency can both point at later and agree on.

    The Work List: Name Each Task And How Often

    Write tasks as verbs with a rhythm. “Apply plugin updates weekly” is a line. “Keep the site up to date” is a wish.

    A work list for a plan usually has four groups:

    The contract in 30 seconds: the vague sentence, the six lines, vague versus named, response times, ownership and the limits.

    Where The Plan Ends: Name What Costs Extra

    Every plan has an edge. Decide where it is before a client finds it for you.

    Common items that sit on the edge:

    • Cleaning up after a hack that happened before the plan started.
    • Restoring a site after a client edit went wrong.
    • Fixing a conflict caused by a plugin the client installed.
    • New pages, new features and content changes.
    • Moving the site to a new host.

    You do not have to charge for all of them. You do have to say which ones you do and which ones you quote. A short line such as “Work outside the list above is quoted before it starts” removes most of the surprise.

    Response Times: Acknowledge Is Not Fix

    Two numbers hide inside every plan. How fast will you reply, and how fast will you fix?

    They are different promises. A reply can be quick and cheap. A fix depends on what broke, and some breaks take a day.

    Name both, and name the hours they apply to. Say what “urgent” means, for example a checkout that does not complete, and how a client reaches you for it. Do not promise a fix time you cannot control. A host outage, a plugin vendor delay or a payment gateway problem sits outside your hands.

    Illustration of the two promises in a maintenance plan, a quick reply and a fix that depends on the cause, plus host outages, vendor delays and gateway problems outside the agency's control
    A reply is a promise about time. A fix is a promise about cause.

    Access And Who Owns What

    Most handover pain starts with ownership. Write down who holds each of these:

    • The domain registration and its renewal.
    • The hosting account.
    • Premium plugin and theme licences, and who pays to renew them.
    • The administrator accounts on the site. Give every person their own login and keep the number of Administrators small.
    • Third-party accounts such as email, analytics and payment gateways.

    The safest default is that the client owns the accounts and you are given access. A plan that depends on your own personal licence for a client’s key plugin will break the day you stop working together.

    Licences are an easy cost to leave unwritten. One r/Wordpress thread, “How much do you charge for Monthly Maintenance?”, starts from a freelancer who plans to include a page builder licence and hosting in the plan. That is exactly the kind of cost that has to be written down.

    “I plan on charging maintenance for client sites which includes my page builder license, hosting, as well as the typical maintenance tasks.”

    r/Wordpress, “How much do you charge for Monthly Maintenance?”, August 2024

    Software versions belong here too. WordPress 7.0 dropped support for PHP 7.2 and 7.3, so the server version is part of the plan, not a side detail. Name who is responsible for it, and see the WordPress PHP version problem for why it matters for security fixes.

    A free “Essentials” maintenance plan template for web designers, freelancers and small agencies, shared with the WPTuts community.

    WPTuts, “New Free Essentials Website Maintenance Plan Template” on YouTube, 24 Jul 2026

    How The Plan Ends

    Contracts are often written as if the plan will last forever. Write the ending too.

    • Notice. How much notice either side gives, and when the final charge lands.
    • Handover. What the client gets on exit: credentials, the latest backup, and a short list of what was done.
    • Cleanup. Which of your tools and accounts are removed from the site, and when.

    A clean exit protects the relationship more than a clean start does. Clients remember how it ended.

    What A Contract Does Not Solve

    A contract is a document. It does not do the work and it does not change how people behave.

    • It does not keep the site healthy. A signed plan with unverified backups is still a risk.
    • It does not replace a conversation. Send a short summary in plain words as well as the contract itself.
    • It does not cover what you cannot control. Host outages, vendor delays and client decisions sit outside the list.
    • It is not legal advice. Liability, data protection and jurisdiction vary. Have a lawyer review the final wording.
    • It does not set the price. That is a separate question, covered in the cost of a maintenance plan.

    A plan that promises everything is easy to sell and hard to keep. Say what you cover, and say what you do not.

    A One-Page Check You Can Run This Week

    Pick three of your current plans and ask these questions of each contract:

    1. Is every task named with how often it runs?
    2. Is there a line that says what costs extra?
    3. Are reply time and fix time written separately?
    4. Does the client own the domain, hosting and licences?
    5. Is there a line for what the plan does not promise?
    6. Is there a notice period and a handover list?

    Then choose one outcome per plan:

    1. Keep it as written.
    2. Add the missing lines and send it for agreement.
    3. Replace it with a clearer one-page version.
    4. Split it into two tiers with clear edges.
    5. Retire it, if it cannot be written down honestly.

    If a plan fails three or more questions, fix that one first.

    Sources And Further Reading

    • Updating WordPress: the official guide to core updates.
    • WordPress Backups: what a backup should include, from the official documentation.
    • Dropping support for PHP 7.2 and 7.3: the WordPress core announcement behind the PHP point above.
    • The video and the r/Wordpress thread in this post: a short look at what a maintenance plan template covers, and a freelancer’s question about what to bundle into a plan.

    The Point

    A maintenance plan is a short list of promises with a size on each one.

    You do not need a long contract. You need a clear one.

    Which line is missing from the plan you sold last?

  • Introducing Protuno: Super Agents For WordPress

    Introducing Protuno: Super Agents For WordPress

    You look after a lot of client sites. You cannot open every one of them every morning.

    And the one that breaks is never the one you opened.

    Short answer: Protuno audits any WordPress site from its address. Connect a site and your own AI app fixes what you approve, with a backup first and Undo after.

    What this post is, and is not: a short tour of what Protuno does, updated on 28 October 2026. Every screen in the film below is the real product.

    The Protuno launch film, recorded from the live product.

    What Protuno Does Today

    Seven things, each one live in the product now.

    WhatWhat it means
    Audit any site54 checks from the address alone. Connect the Protuno plugin and 31 more read from inside the site, 85 in all.
    Read or ExecuteSet per site. Read analyses and changes nothing. Execute may change files, the database and settings.
    Plain-word findingsEach says what to do next, and Fix in Claude opens your AI app with the request already typed in.
    Fix, then proveA backup is taken before every fix, and the fix can be undone from the run report. The next scan decides whether the finding is closed.
    Your own AI appThe AI runs in your own AI app (Claude, ChatGPT, Claude Code, Codex, Cursor and others), on your plan there. Protuno has no chat of its own and no credits.
    27 playbooksJobs written down step by step before they run. 10 can change a site and 17 only read it.
    Routines and reportsRoutines run a playbook on a schedule as a task in your AI app (Pro and Agency). Connectors let your agents read your outside platforms, and Pro and Agency add white-label reports.

    A Real Fix, Start To Finish

    On 5 October, on one of our test sites, Protuno fixed two findings. This is the run, as the report shows it:

    1. Allow search engines to index the site.
    2. Delete the publicly readable debug.log.
    3. Check the site still loads.
    4. Re-run the 2 checks: 2 of 2 confirmed fixed.

    It passed in 6.8 seconds. The last two steps are the point: “fixed” means the site still loads and the original checks agree.

    The Protuno run report for Fix 2 findings: four steps, four done, none failed, passed in 6.8 seconds.
    The real run report: four steps, none failed.

    The Six Super Agents

    Six named Super Agents: Wren (Care), Rook (Security), Dash (Performance), Iris (SEO), Till (eCommerce) and Echo (Marketing). Each owns a group of playbooks and looks after its part of every site, and they are live now, inside your own AI app.

    When this post was first written, on 6 October, they were not live yet, and the post said so plainly. Since 27 October they are.

    Try It On One Site

    Pick the client site you trust least. Paste its address at protuno.com and run the audit. It needs no plugin and changes nothing on the site.

    Run Free Audit →

    The Point

    You cannot open every client site every morning.

    You can know which one needs you.

  • WordPress White Screen Of Death: The Playbook, Step By Step

    WordPress White Screen Of Death: The Playbook, Step By Step

    A white screen is not an error message. It is the absence of one.

    There is no stack trace, no plugin name and no hint. Just an empty page on a site that worked an hour ago.

    That is why the usual advice starts with guessing: rename the plugins folder, switch the theme, raise the memory limit, and see what happens.

    Short answer: a WordPress white screen of death is a symptom with several different causes, and the fix depends on which one you have. WP Doctor is a Protuno playbook that works in five fixed steps: probe how it can reach the site, record a health baseline, match the evidence to a known failure signature, apply one fix behind a snapshot, then re-run the exact check that failed. If it cannot match a signature or safely reach the cause, it hands off instead of guessing.

    What this post is, and is not: it is a practical walk through how a diagnosis playbook is built, based on the WP Doctor playbook as it appears in the Protuno dashboard. It is not a claim that every white screen is fixable automatically, and it does not quote results from a run. Where a cause sits outside WordPress, the honest answer is a hand-off to a human.

    An empty page is a fact about the output. A signature is a fact about the cause.

    Why Deactivate Everything Falls Short

    The folk fix is to deactivate every plugin, then reactivate them one at a time. It works often enough that people trust it. It also hides three problems.

    First, it is a fix without a diagnosis. If the page comes back, you know a plugin was involved. You do not know the fault, or whether a stuck update was the real cause.

    Second, it assumes the cause lives in WordPress. A blank page can also come from the server or a CDN, and plugin shuffling touches neither.

    Third, there is no definition of “fixed”. If the homepage loads, the job feels done. But a homepage that returns 200 while the admin or the REST API is still failing is not a healthy site. This is the same trap described in WordPress rollback after an update: a homepage-only check misses the failures that sit below it.

    A good diagnosis fixes the order. Measure first. Match to a known cause. Change one thing. Check again.

    Three Ways To Look At A Blank Page

    When a site goes white you can approach it from three directions. None is enough alone.

    ViewWhat it tells youWhat it cannot prove alone
    The page itself (status code and body)Whether the response is a 500, an empty 200, a maintenance notice or WordPress’s own critical error messageWhy it happened
    The error log (debug.log)A fatal or parse error, with a file and a lineWhether that error is the cause of this blank page or an old one
    The site’s own state (files, options, tables)A stuck .maintenance file, an update lock, a corrupted permalink option, a table that fails its checkWhether it is currently breaking the page

    A white screen with a fatal in the log and a 500 is a different problem from an empty 200 with a clean log. You want all three views before you touch anything.

    Three views of one blank WordPress page: the page itself, the error log and the site state, each with what it cannot prove alone
    Each view answers one question. None answers all of them.

    The Playbook, Step By Step

    WP Doctor is a Protuno maintenance playbook, shown in the dashboard with a Beta label, five steps, and a “can write” status. It is a general-purpose WordPress diagnosis playbook, so a white screen is one of the problems it is built to handle. It is not on the public playbooks page yet, so treat it as a new playbook that is still being rolled out.

    It runs a fixed sequence. Steps 1 to 3 only read the site. Step 4 is the only one that can change anything, and only behind a backup. Step 5 re-checks and reports.

    The five WP Doctor steps in order: Gateway And Environment Probe, Reproduce And Health Baseline, Collect Evidence And Match Signature, Safety Snapshot And Apply Fix, Verify And Report
    Five steps in a fixed order: three read only, one that can write behind a backup, and a final check.
    #StepWhat it does
    1Gateway And Environment ProbeWorks out how Protuno can reach the site and what it can safely do. Records WordPress and PHP versions, the real table prefix, multisite status, server software and the site URLs. Read only.
    2Reproduce And Health BaselineProbes the homepage, the newest published post, wp-admin and the REST API. Checks the database tables, key options and files, active plugins and theme, and the recent tail of debug.log. Defines what “healthy” means for this site. Read only.
    3Collect Evidence And Match SignatureBundles the evidence and matches it against a catalogue of known failure signatures. Localizes the layer and decides: auto-fix, propose, hand off or escalate. Read only.
    4Safety Snapshot And Apply FixConfirms a backup exists on disk, applies the one fix the diagnosis named, re-checks health, and rolls back automatically if the site got worse. Or applies nothing and hands off.
    5Verify And ReportRe-runs the exact check that failed, then writes a report with a status: fixed, rolled back, handed off, escalated or healthy. Makes no changes.

    The order is the point. The baseline exists before any change, so “did we break something” has an answer. The match exists before the fix, so evidence picks the fix.

    Step 1: Know What You Can Reach

    Before it looks at the white screen, the playbook records how it can reach the site. In the step instructions, access is the Protuno connection running PHP on the site: it can run PHP, query the database, read debug.log and read and write files.

    It cannot run WP-CLI, has no SSH and has no CDN or edge API. Server and edge causes are detect-and-hand-off only. A tool that pretends it can reach everything will eventually “fix” something it cannot see.

    Step 2: Write Down Healthy Before Changing Anything

    The baseline probes the homepage, the permalink of the newest published post, the admin and the REST API, each with a timeout. It then runs a table check on the database, reads the siteurl, home, permalink structure and any core updater lock, and checks whether a .maintenance file and an .htaccess file exist. It lists the active plugins and the active theme, and reads roughly the last 200 lines of the debug log.

    Then it defines its health check. A site passes when the homepage returns 200, the newest post returns 200 (or there are no posts), wp-admin is reachable rather than erroring, the REST API returns 200, and no new PHP fatal or parse error has been added to the log since the baseline.

    That definition is the tripwire for every later step. If step 4 changes something and health gets worse than the baseline, the change is reversed.

    Step 3: Match A Signature, Or Say You Cannot

    This is the step that replaces guessing. The instructions carry a catalogue of known failure signatures, and the playbook matches the evidence to it deterministically. Each signature is a set of signals that must all be true, optional signals that raise confidence, and signals that disqualify the match.

    A few of the WordPress-layer signatures in the catalogue, in plain terms:

    • A stuck maintenance state: a .maintenance file exists, or the homepage shows the “Briefly unavailable for scheduled maintenance” message.
    • A stuck update lock: the core updater lock option is set and there is no maintenance file.
    • A permalink 404: the homepage returns 200, the post returns 404, and a permalink structure is set.
    • A plugin fatal: a PHP fatal or parse error in the log, together with a 500, an empty homepage body or the “critical error” message.
    • A theme conflict: errors in the log that point into the themes folder, with a 500.
    • Others cover a REST API failure, headers already sent, a 403, a login redirect loop and corrupted database tables.

    Server and edge signatures are in the catalogue too, such as a database connection error or a Cloudflare 521 to 524. A 521 to 524 means the origin is down, so the playbook places the cause at the server and hands off.

    Two rules keep it honest. If nothing matches, it escalates and no fix is invented. If two high-confidence matches point at different causes, it escalates rather than picking one.

    The decision that comes out is one of four: auto-fix, propose, hand off or escalate.

    Step 4: One Fix, Behind A Snapshot

    Only now can anything change. The hard gate comes first: the options and files about to be touched are exported and copied, then the playbook reads the backup back from disk to confirm it exists and is not empty. If it is missing or empty, it refuses to apply the change.

    Then it applies one logical fix. That can be more than one PHP call when the calls belong together, such as writing a corrupted permalink option back and then flushing the rewrite rules.

    After the change it re-runs the health check against the baseline. If the site is worse, it restores from the backup, runs the check again to confirm it is back at baseline, and records that it rolled back.

    For a plugin conflict, the fix is a bisect: deactivate the plugins, check health, then reactivate them one by one while checking between each, until the plugin that flips the site from passing to failing is found. It leaves that one deactivated and restores the rest. Every plugin that was active in the baseline and is not the culprit must end active. The site is never left stripped.

    Some things are never applied: changing the site URL on an unconfirmed canonical, repairing InnoDB tables, setting the memory limit blind or touching database credentials. Those go to hand-off with exact steps for a human.

    Step 5: Prove It, Or Do Not Claim It

    The last step re-runs the exact check named in the diagnosis, such as the post URL that returned 404 or the REST endpoint that should answer. Never a softer one. It then picks one honest status: fixed, rolled back, handed off, escalated or healthy. The report names the layer and signature, the evidence that matched, what was applied and anything left for a human. “Fixed” is only allowed when the re-run passes.

    WP Doctor: read first, match a signature, change once, then prove it.

    A Small Test You Can Run Today

    You do not need a playbook to see how a blank page behaves. Record four things before you change a setting:

    • The status code and the length of the body for the homepage, a recent post, the admin and the REST API.
    • Whether a .maintenance file exists in the WordPress root.
    • The last few lines of wp-content/debug.log, if logging is enabled.
    • The active plugin list and the active theme, written down.

    A rough version from a terminal:

    # status code and size for each URL
    for u in https://example.com/ https://example.com/a-recent-post/ 
             https://example.com/wp-admin/ https://example.com/wp-json/; do
      curl -s -o /dev/null -w "%{http_code} %{size_download} $un" "$u"
    done
    
    # a stuck maintenance state and the recent log
    ls -la .maintenance
    tail -n 50 wp-content/debug.log

    Keep that output as your baseline. If the debug log is publicly readable, fix that first; this post on exposed debug logs explains why it matters.

    Which Cause Are You Actually Looking At?

    Different symptoms point at different layers. This is a rough map based on the signatures above, not a promise that a symptom always means one cause.

    If you seeA likely signatureNext action
    “Briefly unavailable for scheduled maintenance”Stuck maintenance stateCheck for the .maintenance file after an interrupted update
    Homepage 200 but posts 404Permalink problemCheck the permalink structure and flush the rewrite rules
    500 plus a fatal error in the logPlugin fatalBisect the plugins with a restore point in place
    “There has been a critical error” with a clean logCritical error, medium confidenceFind the failing component by bisecting, since the log does not name it
    Cloudflare 521 to 524Origin downTreat it as a server problem and hand off
    “Allowed memory size exhausted” in the logMemory exhaustedHand off, since the real limit is set by the host

    What WP Doctor Does Not Solve

    A diagnosis playbook has edges. Being clear about them is part of the design.

    • Server and edge causes. There is no SSH and no edge API in the reach. These are diagnosed and handed off, with exact steps for a human.
    • Failures with no known signature. The decision is to escalate, with the evidence it found. It does not invent a fix.
    • Hacked sites. Malware and SEO spam indicators are routed to malware removal. They are not repaired here.
    • A site with no usable baseline. No baseline means no rollback tripwire, so it changes nothing.

    Questions To Ask Your Current Tooling

    Whatever repairs your broken sites, ask it:

    • How does it define “fixed”, and is that check the one that failed?
    • What happens when its fix makes things worse?
    • Does it say so when the cause is outside WordPress, or does it try anyway?

    If the answers are vague, you are being offered a button, not a process.

    Fixing In The Right Order

    When a fix is possible, order matters more than speed. Two practical rules apply to every case. Take a restore point before the first change, and confirm it can be read back. A backup you have not verified is a file with a hopeful name. Then re-run the check that failed after the change, not a friendlier one.

    If an update caused it, the update process, written out before it runs is the way to have fewer of these.

    A Five-Site Triage You Can Do This Week

    Pick five sites and note, for each:

    • The site and how it is hosted.
    • Whether debug.log is available to read, and whether it is public.
    • Whether a recent restore point exists and has been verified.
    • Whether a homepage-only monitor is your only alert.
    • Who gets the call if it goes white at 2 a.m.

    Then decide for each gap: fix now, test first, replace, remove, accept with an owner and a date, or investigate. A homepage-only monitor notices a white screen and little else. Uptime monitoring that asserts content gives the diagnosis something to start from.

    Why The Baseline Goes Stale

    A baseline describes one moment. After an update, a theme change or a hosting move it is out of date, so record it again. WordPress security monitoring makes the same point about snapshots.

    Where Protuno Fits

    WP Doctor is one of the playbooks in the Protuno dashboard, and it is currently marked Beta. It can change a site, which is why the backup gate, the single fix and the automatic rollback sit in the middle of it. The playbooks that are public today are listed on the Protuno playbooks page.

    The aim is not to remove the human. It is to make the first hour of a bad day a matter of evidence, and to say clearly when the next step is yours.

    The Point

    A white screen is a symptom, not a diagnosis.

    The question is not “which plugin do I turn off”. It is “what did the site look like before it broke, and what is the evidence for the cause”.

    Could you answer that for the next site that goes white?