[{"data":1,"prerenderedAt":993},["ShallowReactive",2],{"blog-index":3},[4,384,689],{"id":5,"title":6,"author":7,"body":8,"category":369,"description":370,"extension":371,"meta":372,"modifiedAt":373,"navigation":374,"path":375,"publishedAt":376,"readingTime":377,"related":378,"seo":381,"stem":382,"__hash__":383},"blog\u002Fblog\u002Fwordpress-autoload-slow-admin.md","Why your WordPress admin got slow, and why it is probably autoload","The Talos team",{"type":9,"value":10,"toc":356},"minimark",[11,15,18,23,31,47,54,57,73,76,80,83,135,138,141,175,178,207,210,214,217,226,232,238,248,252,255,260,267,273,277,280,283,287,290,299,306,309,313,316,320,323,331,334,342,346,349,352],[12,13,14],"p",{},"A slow WordPress admin is one of the more frustrating performance problems, because the usual advice does not apply. Caching plugins do not help — wp-admin is deliberately uncached. A CDN does not help. Upgrading your hosting plan often does not help either, which is how people end up paying more for the same problem.",[12,16,17],{},"The cause, more often than anything else, is autoloaded options. This post explains what that means, how to measure it, and how to fix it without breaking your site.",[19,20,22],"h2",{"id":21},"what-autoload-actually-does","What autoload actually does",[12,24,25,26,30],{},"Every WordPress page load starts by querying the ",[27,28,29],"code",{},"wp_options"," table:",[32,33,38],"pre",{"className":34,"code":35,"language":36,"meta":37,"style":37},"language-sql shiki shiki-themes github-light github-dark","SELECT option_name, option_value FROM wp_options WHERE autoload = 'yes'\n","sql","",[27,39,40],{"__ignoreMap":37},[41,42,45],"span",{"class":43,"line":44},"line",1,[41,46,35],{},[12,48,49,50,53],{},"Everything that returns is loaded into memory and kept for the duration of the request. That is the point — options marked ",[27,51,52],{},"autoload"," are things WordPress expects to need on essentially every request, so fetching them in one query beats fetching them individually.",[12,55,56],{},"The mechanism is sound. The problem is that it is opt-in by default and nobody cleans up.",[12,58,59,60,63,64,67,68,72],{},"When a plugin stores a setting with ",[27,61,62],{},"add_option()",", autoload defaults to ",[27,65,66],{},"yes"," unless the developer explicitly says otherwise. Most do not. So every plugin you have ever installed has probably added its settings to the set of data loaded on every request — and if you deactivated the plugin without deleting its data, ",[69,70,71],"strong",{},"those options are still being loaded",", forever, for a plugin that no longer runs.",[12,74,75],{},"Add a few years of that, plus expired transients that never got cleaned up, and a site can reach several megabytes of data being deserialised on every single request.",[19,77,79],{"id":78},"the-numbers-that-matter","The numbers that matter",[12,81,82],{},"Rough guidance on total autoloaded size:",[84,85,86,99],"table",{},[87,88,89],"thead",{},[90,91,92,96],"tr",{},[93,94,95],"th",{},"Size",[93,97,98],{},"Assessment",[100,101,102,111,119,127],"tbody",{},[90,103,104,108],{},[105,106,107],"td",{},"Under 300 KB",[105,109,110],{},"Fine. Do not spend time here.",[90,112,113,116],{},[105,114,115],{},"300 KB – 800 KB",[105,117,118],{},"Slightly heavy but unlikely to be your bottleneck.",[90,120,121,124],{},[105,122,123],{},"800 KB – 2 MB",[105,125,126],{},"Worth cleaning up. Probably noticeable in the admin.",[90,128,129,132],{},[105,130,131],{},"Over 2 MB",[105,133,134],{},"Almost certainly your problem.",[12,136,137],{},"Entry count matters less than total size, but more than a thousand autoloaded entries usually indicates the same underlying neglect.",[12,139,140],{},"You can measure it directly:",[32,142,144],{"className":34,"code":143,"language":36,"meta":37,"style":37},"SELECT\n  COUNT(*) AS entries,\n  ROUND(SUM(LENGTH(option_value)) \u002F 1024, 1) AS kb\nFROM wp_options\nWHERE autoload = 'yes';\n",[27,145,146,151,157,163,169],{"__ignoreMap":37},[41,147,148],{"class":43,"line":44},[41,149,150],{},"SELECT\n",[41,152,154],{"class":43,"line":153},2,[41,155,156],{},"  COUNT(*) AS entries,\n",[41,158,160],{"class":43,"line":159},3,[41,161,162],{},"  ROUND(SUM(LENGTH(option_value)) \u002F 1024, 1) AS kb\n",[41,164,166],{"class":43,"line":165},4,[41,167,168],{},"FROM wp_options\n",[41,170,172],{"class":43,"line":171},5,[41,173,174],{},"WHERE autoload = 'yes';\n",[12,176,177],{},"And find the specific offenders:",[32,179,181],{"className":34,"code":180,"language":36,"meta":37,"style":37},"SELECT option_name, ROUND(LENGTH(option_value) \u002F 1024, 1) AS kb\nFROM wp_options\nWHERE autoload = 'yes'\nORDER BY LENGTH(option_value) DESC\nLIMIT 20;\n",[27,182,183,188,192,197,202],{"__ignoreMap":37},[41,184,185],{"class":43,"line":44},[41,186,187],{},"SELECT option_name, ROUND(LENGTH(option_value) \u002F 1024, 1) AS kb\n",[41,189,190],{"class":43,"line":153},[41,191,168],{},[41,193,194],{"class":43,"line":159},[41,195,196],{},"WHERE autoload = 'yes'\n",[41,198,199],{"class":43,"line":165},[41,200,201],{},"ORDER BY LENGTH(option_value) DESC\n",[41,203,204],{"class":43,"line":171},[41,205,206],{},"LIMIT 20;\n",[12,208,209],{},"That second query is the useful one. Autoload bloat is rarely evenly distributed — it is usually three or four options doing almost all of the damage.",[19,211,213],{"id":212},"what-you-will-find-at-the-top-of-that-list","What you will find at the top of that list",[12,215,216],{},"In my experience the same categories come up repeatedly.",[12,218,219,222,223,225],{},[69,220,221],{},"Expired transients."," Transients are meant to be temporary cached values, and they are supposed to be cleaned up when they expire. In practice, expired transients frequently linger in ",[27,224,29],{}," indefinitely, particularly on sites without a persistent object cache. On a neglected site these can be the majority of the table. They are safe to delete: a transient is by definition regenerable.",[12,227,228,231],{},[69,229,230],{},"Orphaned plugin settings."," Options from plugins you removed years ago. Deactivating a plugin does not delete its data, and uninstalling often does not either. These are safe to delete once you have confirmed the plugin is genuinely gone, but confirm first — some plugins use a name prefix that does not match their slug.",[12,233,234,237],{},[69,235,236],{},"Genuinely large single options."," Some plugins store a big serialized blob — a licence cache, an import log, a settings array with hundreds of keys. These are usually still in use, so deleting them will break something. The right fix is to switch them off autoload rather than remove them.",[12,239,240,243,244,247],{},[69,241,242],{},"Cron."," The ",[27,245,246],{},"cron"," option holds every scheduled event. On a site with a large cron backlog this grows significantly, which is a good hint to look at whether WP-Cron is actually firing.",[19,249,251],{"id":250},"fixing-it","Fixing it",[12,253,254],{},"Do these in order of risk.",[256,257,259],"h3",{"id":258},"delete-expired-transients","Delete expired transients",[12,261,262,263,266],{},"The safest and usually most effective step. Any half-decent database optimisation plugin does this, or you can do it directly. Every transient has a paired ",[27,264,265],{},"_transient_timeout_"," entry holding its expiry, so expired ones are identifiable rather than guessed at.",[12,268,269,270,272],{},"This alone regularly takes a bloated ",[27,271,29],{}," table down by half or more.",[256,274,276],{"id":275},"remove-orphaned-options","Remove orphaned options",[12,278,279],{},"Cross-reference the largest autoloaded options against your installed plugin list. Anything whose prefix matches a plugin you no longer have is a candidate.",[12,281,282],{},"Take a backup first. The failure mode here is deleting something still in use because the option prefix did not obviously match its owner.",[256,284,286],{"id":285},"switch-large-in-use-options-off-autoload","Switch large in-use options off autoload",[12,288,289],{},"For options that are legitimately large and legitimately needed, but not needed on every request:",[32,291,293],{"className":34,"code":292,"language":36,"meta":37,"style":37},"UPDATE wp_options SET autoload = 'no' WHERE option_name = 'some_large_option';\n",[27,294,295],{"__ignoreMap":37},[41,296,297],{"class":43,"line":44},[41,298,292],{},[12,300,301,302,305],{},"The option remains fully available via ",[27,303,304],{},"get_option()"," — WordPress simply fetches it on demand instead of preloading it. For an option only read on one admin screen, that is a straight win.",[12,307,308],{},"Be careful with anything read on the front end during a normal page render, since you would trade one autoload query for a per-request individual query. Options only touched in wp-admin are the safe candidates.",[256,310,312],{"id":311},"add-a-persistent-object-cache","Add a persistent object cache",[12,314,315],{},"If your host supports Redis or Memcached, a persistent object cache changes the economics entirely: autoloaded options get served from memory rather than re-queried on every request. It does not make the bloat disappear, but it dramatically reduces what the bloat costs you.",[19,317,319],{"id":318},"verify-with-a-before-and-after","Verify with a before and after",[12,321,322],{},"This is the part people skip, and it is the part that tells you whether any of it worked.",[12,324,325,326,330],{},"Record the numbers before you start: total autoloaded size, entry count, and a timed loopback request to the site. Then re-measure after. Without a baseline you are relying on whether the admin ",[327,328,329],"em",{},"feels"," faster, which is not a measurement — expectation bias after an hour of database work is substantial.",[12,332,333],{},"A typical result on a badly bloated site looks like autoload dropping from around 2.4 MB to under 500 KB, with admin page loads improving by well over a second. That is a large enough change to be unambiguous.",[12,335,336,341],{},[337,338,340],"a",{"href":339},"\u002Fwordpress-site-health","Talos measures all of this in one pass"," — autoload size and entry count, the largest offenders, expired transient ratio, plus cache state and loopback timing — and can diff two diagnostic snapshots so the before-and-after is a concrete comparison rather than an impression.",[19,343,345],{"id":344},"keeping-it-clean","Keeping it clean",[12,347,348],{},"Autoload bloat is not a one-time fix, because the cause is ongoing: you will keep installing and removing plugins, and each cycle leaves residue.",[12,350,351],{},"The realistic approach is a recurring check rather than a resolution. A monthly read-only report on autoload size, entry count and the top offenders takes no effort once it exists, and it turns a slow degradation you would not otherwise notice into a number you can watch. When it drifts back over a megabyte, you already know which options to look at.",[353,354,355],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":37,"searchDepth":153,"depth":159,"links":357},[358,359,360,361,367,368],{"id":21,"depth":153,"text":22},{"id":78,"depth":153,"text":79},{"id":212,"depth":153,"text":213},{"id":250,"depth":153,"text":251,"children":362},[363,364,365,366],{"id":258,"depth":159,"text":259},{"id":275,"depth":159,"text":276},{"id":285,"depth":159,"text":286},{"id":311,"depth":159,"text":312},{"id":318,"depth":153,"text":319},{"id":344,"depth":153,"text":345},"Performance","A slow wp-admin usually is not the server. It is autoloaded options loading on every request. Here is how to measure it and what to do about it.","md",{},null,true,"\u002Fblog\u002Fwordpress-autoload-slow-admin","2026-07-29","7 min read",[339,379,380],"\u002Fwordpress-troubleshooting","\u002Fwordpress-maintenance-automation",{"title":6,"description":370},"blog\u002Fwordpress-autoload-slow-admin","p29dmmxIGnmCeVGw9cn0by3Jr9nloP2_NNTSPQ5cnl4",{"id":385,"title":386,"author":7,"body":387,"category":679,"description":680,"extension":371,"meta":681,"modifiedAt":373,"navigation":374,"path":682,"publishedAt":683,"readingTime":684,"related":685,"seo":686,"stem":687,"__hash__":688},"blog\u002Fblog\u002Fsigns-wordpress-site-hacked.md","Seven signs your WordPress site has been hacked, and how to check each one",{"type":9,"value":388,"toc":668},[389,392,395,399,405,408,422,433,443,447,452,461,467,471,476,479,488,491,495,500,503,508,511,515,520,537,540,544,549,552,561,565,574,577,586,590,597,600,603,610,614,617,663,666],[12,390,391],{},"The hardest part of a suspected WordPress compromise is the uncertainty. Something is off — a visitor mentions a redirect you cannot reproduce, your mail starts landing in spam — but you have no idea whether you are looking at an attack, a misconfigured plugin, or a caching artefact.",[12,393,394],{},"This is a checklist for resolving that uncertainty. Each sign comes with what actually causes it and how to confirm it with evidence rather than a hunch.",[19,396,398],{"id":397},"_1-redirects-that-only-some-visitors-see","1. Redirects that only some visitors see",[12,400,401,404],{},[69,402,403],{},"What you notice:"," A customer reports being sent to a spam domain. You visit the same page and it is fine.",[12,406,407],{},"This is the most common signature of a real compromise, and the inconsistency is deliberate. Injected redirect code typically checks the user agent, the referrer, or a cookie before firing. It skips logged-in users and often skips known crawler user agents, specifically so the site owner does not see it.",[12,409,410,413,414,417,418,421],{},[69,411,412],{},"How to check:"," Visit in a private window with a Google referrer, and try a mobile user agent. But the reliable method is to search the source rather than reproduce the behaviour. Look for conditional logic keyed on ",[27,415,416],{},"HTTP_USER_AGENT"," or ",[27,419,420],{},"HTTP_REFERER"," near a redirect, and for obfuscated blocks:",[32,423,427],{"className":424,"code":425,"language":426,"meta":37,"style":37},"language-php shiki shiki-themes github-light github-dark","eval(base64_decode('...'))\n","php",[27,428,429],{"__ignoreMap":37},[41,430,431],{"class":43,"line":44},[41,432,425],{},[12,434,435,438,439,442],{},[27,436,437],{},"eval"," combined with ",[27,440,441],{},"base64_decode"," has almost no legitimate use in a WordPress plugin. It is the single highest-signal pattern to grep for.",[19,444,446],{"id":445},"_2-an-administrator-account-you-do-not-recognise","2. An administrator account you do not recognise",[12,448,449,451],{},[69,450,403],{}," A user in the admin list you did not create, often with a plausible name and a throwaway email.",[12,453,454,456,457,460],{},[69,455,412],{}," Users → All Users, filter by Administrator, and sort by registration date. Anything created around the time symptoms started deserves scrutiny. Be aware that sophisticated attacks hide the account from the user list entirely by filtering ",[27,458,459],{},"pre_user_query",", so an empty list is not proof. Counting administrator rows directly is more reliable than trusting the admin screen.",[12,462,463,466],{},[69,464,465],{},"Do not simply delete it."," Deleting the account removes your evidence and does nothing about the mechanism that created it — which will just create another. Demote and lock it, find the entry point, then clean up.",[19,468,470],{"id":469},"_3-mail-suddenly-going-to-spam","3. Mail suddenly going to spam",[12,472,473,475],{},[69,474,403],{}," Contact form notifications stop arriving. Customers say order confirmations went to junk.",[12,477,478],{},"Compromised WordPress sites are valuable primarily as mail relays. A site sending thousands of spam messages gets its IP and domain reputation destroyed within days, which is why your own legitimate mail stops being delivered.",[12,480,481,483,484,487],{},[69,482,412],{}," Two things point at this. First, check your mail queue or your host's outbound mail logs for volume you cannot account for. Second, check ",[27,485,486],{},"wp_mail"," usage — a compromise often hooks into WordPress's own mail function so the spam inherits your configured SMTP credentials.",[12,489,490],{},"Worth ruling out first: this symptom also has an entirely innocent cause, which is sending mail from your host's IP with no SPF or DKIM record. Check your DNS before assuming the worst.",[19,492,494],{"id":493},"_4-cron-jobs-you-did-not-schedule","4. Cron jobs you did not schedule",[12,496,497,499],{},[69,498,403],{}," Nothing, usually. This one is invisible unless you look.",[12,501,502],{},"This is the sign I would check earliest, because it is both high-signal and easy to verify. Persistence is the attacker's main problem: you might clean the injected file, so they need a mechanism to put it back. A scheduled WordPress event is the standard solution.",[12,504,505,507],{},[69,506,412],{}," Inspect your WP-Cron events and look for hooks that do not belong to any plugin you recognise. Randomised or vaguely-plausible hook names are typical. An unrecognised recurring event is close to conclusive on its own.",[12,509,510],{},"This also explains the most demoralising experience in WordPress cleanup: you remove the malicious code, verify the site is clean, and it is back the next morning. You removed the payload and left the mechanism.",[19,512,514],{"id":513},"_5-file-modification-dates-that-make-no-sense","5. File modification dates that make no sense",[12,516,517,519],{},[69,518,403],{}," A theme file modified last Tuesday when nobody touched the site.",[12,521,522,524,525,528,529,532,533,536],{},[69,523,412],{}," Sort plugin and theme directories by modification time. Injected code is usually appended to the top or bottom of an existing legitimate file rather than added as a new file, because a new file is more conspicuous. ",[27,526,527],{},"wp-config.php",", ",[27,530,531],{},"functions.php"," and ",[27,534,535],{},"index.php"," are the usual targets.",[12,538,539],{},"WordPress core is easier: Site Health reports on modified core files, and core is checksummed against the official release. Unexpected core modification is unambiguous.",[19,541,543],{"id":542},"_6-error-log-entries-from-paths-that-should-not-exist","6. Error log entries from paths that should not exist",[12,545,546,548],{},[69,547,403],{}," PHP warnings referencing files or directories you do not recognise.",[12,550,551],{},"Malicious code is often written carelessly and throws warnings. That makes your error log one of the better detection tools available, and almost nobody reads it.",[12,553,554,556,557,560],{},[69,555,412],{}," Read the log tail and group entries by type rather than scanning chronologically — a thousand repeated lines collapse into one finding with a count, and the unusual entry becomes visible. Look for warnings referencing paths in ",[27,558,559],{},"uploads",", which should never contain executable PHP.",[19,562,564],{"id":563},"_7-search-results-showing-pages-you-never-published","7. Search results showing pages you never published",[12,566,567,569,570,573],{},[69,568,403],{}," A ",[27,571,572],{},"site:"," search on your domain returns pharmacy or casino pages.",[12,575,576],{},"This is spam injection, and it is often invisible when browsing normally because the injected content is served only to crawler user agents. By the time it appears in search results it has usually been running for weeks.",[12,578,579,581,582,585],{},[69,580,412],{}," Search ",[27,583,584],{},"site:yourdomain.com"," and look for anything unfamiliar. Check Google Search Console for a sudden jump in indexed pages, and for any manual action notice. Then fetch a page with a Googlebot user agent and compare the response to what you see in a browser.",[19,587,589],{"id":588},"confirming-it-properly","Confirming it properly",[12,591,592,593,596],{},"Individually, most of these signs have innocent explanations. A modified file might be a legitimate update. Spam-foldered mail might be a missing DNS record. What distinguishes an actual compromise is ",[69,594,595],{},"several signs corroborating each other",", usually clustered around the same dates.",[12,598,599],{},"That is the case for reading the source rather than trusting a scan verdict. A signature scanner tells you something matched a pattern. It does not tell you what the code does, whether it is a false positive, or what else in the site is connected to it — and false positives are common enough in commerce plugins that a lone match proves very little.",[12,601,602],{},"What you want, for each finding, is the file, the line number, the surrounding code, and an explanation of what it does. That is enough to make a judgement yourself.",[12,604,605,609],{},[337,606,608],{"href":607},"\u002Fwordpress-malware-scan","Talos scans plugin and theme source directly"," and reports findings with file and line evidence, then captures every cleanup step in a reversible change set — so an over-aggressive cleanup is a rollback rather than a restore from backup.",[19,611,613],{"id":612},"if-it-is-real","If it is real",[12,615,616],{},"Order matters here.",[618,619,620,627,636,642,651,657],"ol",{},[621,622,623,626],"li",{},[69,624,625],{},"Do not delete anything yet."," Take a backup of the compromised state. It is your evidence, and you may need it to work out the entry point.",[621,628,629,632,633,635],{},[69,630,631],{},"Find the persistence mechanism first."," Cron events, mu-plugins, ",[27,634,527],{}," includes. If you clean the payload and leave the mechanism, it comes back.",[621,637,638,641],{},[69,639,640],{},"Clean reversibly."," Aggressive cleanup causes its own outages. Track what you remove so you can put it back if the site breaks.",[621,643,644,647,648,650],{},[69,645,646],{},"Rotate every credential."," Database password, WordPress salts in ",[27,649,527],{},", all admin passwords, API keys, hosting panel access.",[621,652,653,656],{},[69,654,655],{},"Update everything."," The entry point was almost certainly a known vulnerability in an outdated plugin.",[621,658,659,662],{},[69,660,661],{},"Then scan server-side."," Anything running inside WordPress can only see what WordPress can see. A compromise outside the WordPress directory — or in another site on the same shared account — needs your host's tooling.",[12,664,665],{},"That last point is worth taking seriously. If the same hosting account runs several WordPress installs, assume all of them are compromised until you have checked each one. Cross-site infection through a shared parent directory is one of the most common reinfection routes there is.",[353,667,355],{},{"title":37,"searchDepth":153,"depth":159,"links":669},[670,671,672,673,674,675,676,677,678],{"id":397,"depth":153,"text":398},{"id":445,"depth":153,"text":446},{"id":469,"depth":153,"text":470},{"id":493,"depth":153,"text":494},{"id":513,"depth":153,"text":514},{"id":542,"depth":153,"text":543},{"id":563,"depth":153,"text":564},{"id":588,"depth":153,"text":589},{"id":612,"depth":153,"text":613},"Security","Redirects, spam mail, unfamiliar admins and mystery cron jobs. A practical checklist for confirming a WordPress compromise with actual evidence.",{},"\u002Fblog\u002Fsigns-wordpress-site-hacked","2026-07-22","10 min read",[607,379,339],{"title":386,"description":680},"blog\u002Fsigns-wordpress-site-hacked","1fU-8zAHFTWdPrwEKhTXihuqS1zjMncY023CjnMXxT8",{"id":690,"title":691,"author":7,"body":692,"category":981,"description":982,"extension":371,"meta":983,"modifiedAt":373,"navigation":374,"path":984,"publishedAt":985,"readingTime":986,"related":987,"seo":990,"stem":991,"__hash__":992},"blog\u002Fblog\u002Frun-wordpress-site-on-autopilot.md","How to run your WordPress site on autopilot without losing control",{"type":9,"value":693,"toc":971},[694,697,700,704,707,734,737,741,744,751,754,762,765,769,772,775,781,786,789,793,796,799,804,807,811,814,820,830,837,843,846,850,853,859,865,871,877,881,884,965,968],[12,695,696],{},"There is a version of \"automate your WordPress site\" that means enabling auto-updates and hoping. That is not automation, it is deferral. The updates still happen unsupervised, nothing verifies the site afterwards, and when something breaks at 3am you find out from a customer.",[12,698,699],{},"Real automation needs three things that auto-updates do not have: a defined scope, a verification step, and a report. This post walks through how to build that with Talos workflows, and — more importantly — which work is actually worth automating and which is not.",[19,701,703],{"id":702},"start-with-the-work-that-is-invisible-until-it-fails","Start with the work that is invisible until it fails",[12,705,706],{},"The best automation candidates are checks nobody does because nothing prompts them. On most WordPress sites that list looks like this:",[708,709,710,716,722,728],"ul",{},[621,711,712,715],{},[69,713,714],{},"Overdue cron events."," WP-Cron only fires on requests. On a low-traffic site, scheduled posts silently miss their slot and queued email never sends.",[621,717,718,721],{},[69,719,720],{},"Autoload pressure."," Every autoloaded option loads on every single request. Nothing in the WordPress admin tells you when that has grown to two megabytes.",[621,723,724,727],{},[69,725,726],{},"Error log volume."," A log that grows tenfold in a week is the earliest warning you get for most problems, and nobody reads it.",[621,729,730,733],{},[69,731,732],{},"Security-relevant plugin updates."," Twelve pending updates, one of which patches an actively exploited vulnerability, and no fast way to tell which.",[12,735,736],{},"None of these are urgent on any given day. All of them are the cause of something urgent eventually.",[19,738,740],{"id":739},"the-three-part-structure-of-a-safe-workflow","The three-part structure of a safe workflow",[12,742,743],{},"A Talos workflow is a prompt, a trigger, and a set of pre-authorized scopes. The scopes are the part that matters most, so start there.",[12,745,746,747,750],{},"When you enable a workflow, it stores the action scopes it is allowed to use. A scheduled run executes inside those scopes and ",[69,748,749],{},"cannot widen them",". It cannot add tools, and it cannot change who the email report goes to. This is what makes unattended running defensible: the worst-case behaviour of the workflow is bounded by something you set in advance, not by how the model behaves on the day.",[12,752,753],{},"For a weekly health check, the scopes are:",[32,755,760],{"className":756,"code":758,"language":759},[757],"language-text","site:read\nplugins:read\nemail:send\n","text",[27,761,758],{"__ignoreMap":37},[12,763,764],{},"That is a workflow which physically cannot change your site. It reads, it summarises, it emails. If you are nervous about automation, this is where to start — and honestly, read-only reporting is where most of the value is anyway.",[256,766,768],{"id":767},"the-prompt-is-a-brief-not-a-command","The prompt is a brief, not a command",[12,770,771],{},"The instinct is to write the prompt like a script: \"run this, then this\". You get better results describing the outcome and the judgement you want applied.",[12,773,774],{},"Compare these two:",[776,777,778],"blockquote",{},[12,779,780],{},"Check Site Health. Read the error log. Check cron. Email me.",[776,782,783],{},[12,784,785],{},"Check Site Health, overdue cron events, error log volume since last week, and autoload pressure. Summarize anything that changed for the worse and email me the report. If nothing has meaningfully regressed, say so in one line.",[12,787,788],{},"The second gets you a useful report instead of a data dump, because it tells the agent what \"worth reporting\" means. That last sentence is doing real work: without it, you get a full report every week, you stop reading them by week three, and the automation has failed even though it is running perfectly.",[256,790,792],{"id":791},"choose-exception-based-reporting","Choose exception-based reporting",[12,794,795],{},"The single biggest determinant of whether a scheduled workflow stays useful is whether its output earns attention. A report that arrives every Monday saying \"everything is fine\" trains you to ignore Monday emails.",[12,797,798],{},"Ask for silence when nothing is wrong:",[776,800,801],{},[12,802,803],{},"Only email me if something moved materially in the wrong direction.",[12,805,806],{},"Now the arrival of the email is itself the signal.",[19,808,810],{"id":809},"schedules-versus-hooks","Schedules versus hooks",[12,812,813],{},"Talos supports two trigger types, and picking the wrong one is a common mistake.",[12,815,816,819],{},[69,817,818],{},"Schedules"," — once, daily, weekdays, weekly or monthly, in your site timezone — are right for periodic review. Health checks, update triage, content audits, client reports.",[12,821,822,825,826,829],{},[69,823,824],{},"Hooks"," fire on a WordPress action: content published, comment posted, user registered, plugin activated. These are right when the work should happen ",[327,827,828],{},"because something happened",", not on a timer. Comment triage is the obvious case: triaging on posting keeps the queue permanently near zero, where a nightly sweep lets it fill up for 24 hours.",[12,831,832,833,836],{},"One caveat on schedules. Workflows are queued by WP-Cron, so a workflow due at 09:00 runs on the first cron tick at or after 09:00. On a quiet site, that might be 11:20. If timing matters, disable request-driven WP-Cron and point a server scheduler at ",[27,834,835],{},"wp-cron.php"," every minute:",[32,838,841],{"className":839,"code":840,"language":759},[757],"*\u002F1 * * * * curl -s https:\u002F\u002Fexample.com\u002Fwp-cron.php > \u002Fdev\u002Fnull 2>&1\n",[27,842,840],{"__ignoreMap":37},[12,844,845],{},"The workflow queue deduplicates occurrences, so overlapping cron requests will not double-run anything.",[19,847,849],{"id":848},"what-not-to-automate","What not to automate",[12,851,852],{},"This is the part most automation advice skips.",[12,854,855,858],{},[69,856,857],{},"Anything requiring editorial judgement about your business."," An agent can find every post missing a meta description. It should not decide unsupervised what your product page says.",[12,860,861,864],{},[69,862,863],{},"Anything you cannot verify afterwards."," If you cannot tell whether the automation did the right thing, you have not removed work, you have hidden it.",[12,866,867,870],{},[69,868,869],{},"Destructive operations on a schedule."," Plugin deletion, user removal, content purges. Talos classifies these as high impact and gates them behind explicit confirmation for a reason. Automating a confirmation gate away defeats its purpose.",[12,872,873,876],{},[69,874,875],{},"The first run of anything."," Run it manually, read the output carefully, then schedule it. Workflows support run-now precisely so you can watch a run before trusting it.",[19,878,880],{"id":879},"a-sensible-starting-set","A sensible starting set",[12,882,883],{},"If you want a concrete place to begin, these four cover most of the value with almost no risk, because three of them are read-only:",[84,885,886,899],{},[87,887,888],{},[90,889,890,893,896],{},[93,891,892],{},"Workflow",[93,894,895],{},"Trigger",[93,897,898],{},"Scopes",[100,900,901,917,935,949],{},[90,902,903,906,909],{},[105,904,905],{},"Monday health check",[105,907,908],{},"Weekly, Mondays 09:00",[105,910,911,528,914],{},[27,912,913],{},"site:read",[27,915,916],{},"email:send",[90,918,919,922,925],{},[105,920,921],{},"Update triage",[105,923,924],{},"Weekly, Mondays 08:00",[105,926,927,528,930,528,933],{},[27,928,929],{},"plugins:read",[27,931,932],{},"web:research",[27,934,916],{},[90,936,937,940,943],{},[105,938,939],{},"Error log watch",[105,941,942],{},"Daily, 07:00",[105,944,945,528,947],{},[27,946,913],{},[27,948,916],{},[90,950,951,954,957],{},[105,952,953],{},"Comment triage",[105,955,956],{},"Hook, on comment posted",[105,958,959,528,962],{},[27,960,961],{},"content:read",[27,963,964],{},"comments:moderate",[12,966,967],{},"Only the last one writes anything, and its worst case is a comment left pending.",[12,969,970],{},"Add write scopes later, one at a time, once you have watched the read-only version behave for a few weeks. The point of automation is not to remove yourself from the loop entirely. It is to stop being the thing that remembers.",{"title":37,"searchDepth":153,"depth":159,"links":972},[973,974,978,979,980],{"id":702,"depth":153,"text":703},{"id":739,"depth":153,"text":740,"children":975},[976,977],{"id":767,"depth":159,"text":768},{"id":791,"depth":159,"text":792},{"id":809,"depth":153,"text":810},{"id":848,"depth":153,"text":849},{"id":879,"depth":153,"text":880},"Automation","Most WordPress maintenance is the same handful of checks done irregularly. Here is how to turn each one into a scheduled workflow with permissions that cannot expand.",{},"\u002Fblog\u002Frun-wordpress-site-on-autopilot","2026-07-14","8 min read",[988,989,380],"\u002Fwordpress-autopilot","\u002Fwordpress-ai-workflows",{"title":691,"description":982},"blog\u002Frun-wordpress-site-on-autopilot","CuAbG4w6GYIOTkaiFmd-GcJinzshz7pwoyrI8Od2nP0",1789114981527]