Inspect Suspicious Cron Jobs After a WordPress Hack
Review WP-Cron events and hosting cron separately, preserve the evidence, trace each hook to its owner, and disable only confirmed malicious jobs.
Malicious files or redirects return hours after an initial cleanup. The event list contains a hook nobody recognizes, but it could also belong to backups, an email queue, inventory synchronization, or a plugin whose hook name is not obvious. Deleting every scheduled task might stop the visible symptom—and also break orders, notifications, or backups.
Short answer: inspect WordPress events with WP-CLI, then review hosting or system cron as a separate source. Preserve both lists and their collection times, trace each hook to its code and owner, and compare the findings with deployment and host logs. Do not run a suspicious task as a test. Once evidence supports the finding, disable the specific job and address the code or account that created it; then monitor for recreation.
For other signs of unauthorized access, start with the WordPress hacked-site response guide. To check whether malware symptoms are returning, see signs of a compromised website and the WordPress security guide.
Understand the two schedulers
WP-Cron is WordPress’s event scheduler. Events are stored by WordPress and are generally processed when a request triggers a schedule check; WP-Cron is not itself a continuously running daemon. Plugins and themes register recurring hooks for legitimate work. The WordPress Cron handbook explains this model and how a plugin can schedule an event.
A system cron—such as a Linux crontab or a task configured in a hosting panel—is managed outside WordPress. A host, deployment tool, backup service, or an attacker with operating-system access could add a job there. Hosting providers sometimes use a system scheduler to call wp-cron.php, as described in the WordPress guide to using a system task scheduler. Finding a scheduler that calls WordPress is therefore not enough to label it malicious; identify its owner and purpose.
Do not treat the absence of an event in one list as proof that no scheduled persistence exists. Someone with hosting access can schedule PHP directly, while WordPress may recreate an event when a plugin loads. Keep two inventories and record where each came from: WP-CLI, the host panel, a Unix account, or another scheduler.
Reduce risk and preserve what you find
Confirm that you have authorized access and ask the host for a snapshot if compromise is suspected. Record the server time zone, collection time, site and environment, operating-system user, WordPress version, and person accessing the panel. Store command output somewhere private; cron lines can expose internal paths, tokens, or other details that should not go into a public ticket.
Do not copy, share, or execute a command you have not understood. A cron entry may contain a private URL or a destructive command. Avoid broad cleanup during order processing, critical jobs, or a backup window without the service owner’s approval. If host logs rotate quickly, ask the provider to retain a copy.
Create an expected-job inventory from deployment notes, plugin and theme documentation, approved backup schedules, and operations records. Ask the system owner before classifying a task as unknown. A random-looking hook deserves investigation, but a familiar name does not prove that its command or callback is legitimate.
List WordPress events without running them
Use WP-CLI from the intended installation to read scheduled events:
wp cron event list --fields=hook,next_run_gmt,next_run_relative,recurrence
This list shows hook names, upcoming times, and recurrence information; it does not by itself identify every callback or the person who registered it. Save the output, repeat the read later to spot changes, and compare unfamiliar events with the first observed symptom. If several installations or a multisite share the host, verify the path and site context before acting.
Do not run wp cron event run <hook> to test an untrusted event. It can send messages, delete data, execute malicious code, or change database state. Do not delete the cron option directly with SQL either; that can erase WordPress schedules in bulk and make the original state harder to reconstruct.
A hook is normally called by plugin or theme code. Search a safe, read-only copy for the exact name. For example:
grep -R --include='*.php' -n 'hook_name_under_review' \
wp-content/plugins wp-content/themes wp-content/mu-plugins
Replace the example with the exact hook and confirm the paths belong to this installation. If no code is found, the event may have been registered by a removed plugin, a must-use plugin, encrypted code, database changes, or a component that exists only in another release. A blank result is not proof of malware. Ask the maintainer to explain the callback, its inputs, its effects, and the supported way to remove the event.
Look for legitimate hooks scheduled at an unexpected time or frequency, callbacks that do not match documentation, arguments containing a URL or token, and events that reappear after removal. Protect sensitive arguments when sharing a report; redact the token but retain enough information to show that the value was reviewed.
Review hosting and operating-system cron
Ask an authorized hosting or systems administrator to inspect the scheduler for the site’s account, deployment users, and relevant system services. On Linux, crontab -l lists the crontab for the current user; running it as root does not automatically show the application’s user schedule. Confirm the account context with the operator, inspect the hosting control panel, and ask the provider about global jobs or managed schedulers that are not visible to your account.
Never use crontab -r as a cleanup shortcut. It removes the current user’s crontab and can stop backups, updates, reports, and other organizational jobs. Do not edit a global scheduler without host authorization. Record the owner, schedule, executable path, working directory, logs/output, and documented purpose. Follow the provider’s process for preserving a copy before any change.
For an unfamiliar command, ask the host or application owner whether it belongs to managed backups, uptime monitoring, a queue, or deployment. If it points to a new file in a temporary directory, do not execute it. Ask the host to inspect a preserved copy and report which account created it, when it changed, and whether it is still active.
Establish ownership and impact before disabling anything
Ask the same questions for each job: which component registered it, which account created or runs it, what data does it read or write, is there a change ticket, does the timing make sense, and which services depend on it? Compare answers with the plugin owner, vendor, operations team, and host records. If no one can identify an owner, raise the concern—but do not delete it simply because an answer is not immediately available.
An event might process a queue or transaction. Disabling a backup task can leave no usable recovery point. Before intervening, agree on the service impact, change window, rollback path, monitoring owner, and how to restore the job if it turns out to be required. Involve the application owner and host when the site handles orders or a critical service.
If several indicators point to active persistence—unfamiliar code, an unknown file writer, and a recurring job with no owner—ask an incident-response professional to plan containment and collection. Record each change, avoid spreading malware samples, and do not begin broad cleanup that destroys the sequence of events.
Disable only a confirmed finding
Once the owner and source are understood, use the component’s documented controls or work with the host to stop the specific task. WP-CLI provides wp cron event delete <hook> to delete events for the named hook. The WP-CLI command reference notes that it deletes all events for that hook, so preserve the schedule list and confirm there is no legitimate use of the same hook before running it.
Removing an event does not remove the code that registered it. A plugin may schedule it again on the next request, and a system cron may continue calling the same process. Address the verified source, disable a confirmed malicious component using an approved procedure, or ask the host to block one specific line with rollback ready. Do not permanently disable all WP-Cron or every server scheduler as a malware fix.
After a change, collect the event list again and monitor it through the next expected run time. Check whether the hook returns, related files reappear, or logs identify a process that made another change. If it returns, capture the new time and snapshot difference, then find the registration source or still-active credential. Repeating deletion without identifying that source is not a resolution.
Recover and prevent repeat findings
After stopping a malicious job, investigate the vulnerability, account, or component that allowed it to be created. Review administrator users, permissions, plugins and themes, relevant keys and tokens, logs, and changed files. Rotate exposed credentials from a clean device, invalidate unauthorized sessions, and update components after a backup and compatibility review. Record each action and the verification result.
Make future reviews easier by documenting the owner and purpose of each scheduled task, keeping scheduler configuration with deployment records, limiting shell access, using separate service accounts where appropriate, and monitoring schedule changes. Test backups regularly so a legitimate job can be restored deliberately. No single scanner or event list replaces application and host monitoring.
Frequently asked questions
Is every unfamiliar WP-Cron event malware? No. Plugins, themes, and integrations can register technical-looking hooks. Identify the callback and owner, match the installed version and documentation, and preserve the result before changing anything.
Can I delete all cron jobs to stop malware? That can break important work and will not remove code that registers events again. Check WordPress and hosting schedulers separately, then stop only a job supported by evidence.
What is the difference between deleting a WordPress event and clearing a crontab? wp cron event delete removes events for a specified WordPress hook. crontab -r can remove every scheduled job for the current system user. Neither action replaces tracing the source and assessing impact.
What if the suspicious job comes back? Save the new list and timestamp, identify the plugin/code or system account that recreated it, ask the host to review logs, and escalate possible persistence. Do not keep deleting it without closing the source.
If an unfamiliar task runs as the hosting user, calls a tokenized URL, or recreates a file or redirect, contact the website security response team. Share a redacted hook name, times, the source of each list, and actions taken—not passwords, tokens, or a dangerous command in a public space.
Does any of this look like your website?
Tell us on WhatsApp. We look first, explain what needs fixing, then you decide. For a fuller picture, request the free audit.
Related articles.
How to Compare WordPress Files Changed After an Incident
Compare WordPress files with the right release checksums, preserve a snapshot first, and investigate core, plugin, theme, and custom-code changes.
One Website Page Redirects to an Unfamiliar Domain: How to Trace It
When one URL redirects visitors, record the first response, map it to a WordPress object, and distinguish page-level rules from server, script, or compromise.
WordPress Redirects Only on Mobile: How to Investigate
A WordPress redirect that appears only on mobile needs safe reproduction, log, cache, server and code checks, then verification without exposing visitors.