[{"data":1,"prerenderedAt":258},["ShallowReactive",2],{"seo-verification":3,"blog-hacked-client-wordpress-site-an-agency-recovery-procedure-en":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-hacked-client-wordpress-site-an-agency-recovery-procedure-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":28,"featuredImage":30,"bgImage":31,"posterImage":32,"relatedSolution":33,"intro":36,"sections":37,"ctaTitle":205,"ctaBody":206,"ctaButton":207,"ctaUrl":208,"relatedPosts":209},544,"hacked-client-wordpress-site-an-agency-recovery-procedure",{"fr":12,"en":10,"ar":13,"es":14},"site-wordpress-pirate-client-agence-procedure-reprise","موقع-ووردبريس-مخترق-لعميل-وكالة-إجراء-الاستعادة","sitio-wordpress-hackeado-cliente-agencia-procedimiento-recuperacion","Hacked client WordPress site: an agency recovery procedure","Isolate without destroying evidence, choose between cleaning, restoring or rebuilding, reopen with renewed access and inform the client. wp-cli commands included.",12,0,false,"2026-10-31T00:00:00+00:00","2026-10-31T19:07:30+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},8,"Security & Monitoring","securite-monitoring","bg-rose-500\u002F10 text-rose-400","security",[29],{"id":23,"name":24,"slug":25,"color":26,"icon":27},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fsite-wordpress-pirate-client-agence-procedure-reprise-poster.svg",{"categorySlug":34,"appSlug":35},"cms-websites","wordpress","The client calls: their site redirects visitors, the browser shows a warning or the host has suspended the account. Without a written procedure, the agency improvises within the hour, and that is when evidence is lost, an already infected backup is restored, or the client is promised a fix that nothing guarantees. This guide walks through a generic procedure, independent of any specific vulnerability: isolate, check, decide on written criteria, reopen with renewed access, then inform the client with a dated message.",[38,42,52,74,98,102,105,137,161,170,185,188,196,202],{"type":39,"title":40,"body":41},"h2","The phone rings: what you know and what you do not","The client describes a symptom, rarely a cause: the site redirects visitors, sometimes on mobile only; the browser shows \"Deceptive site ahead\"; Google flags hacked content; the host has suspended the account. The official WordPress documentation points out that \"hacked\" is an ambiguous term: first record the indicators of compromise (site listed by a search engine, account disabled, users nobody created). Write down the time of the call, the time zone, the client's own words and the latest known changes: a new plugin, a changed theme, access given to a contractor. This is the first line of the incident log.",{"type":43,"title":44,"items":45},"ul","Six rules to set from the first call",[46,47,48,49,50,51],"**One incident lead** — a single person decides, writes to the log and speaks to the client; everyone else executes.","**One voice towards the client** — the client does not receive two versions of the facts.","**A dated log** — every action is recorded with the UTC time, the author, the command run and its result.","**Nothing is deleted before it is copied** — a deleted file no longer tells you how the attacker got in.","**No live fixes** — isolate, analyse, then decide.","**No promise about the cause or the duration** — \"fixed\" is only said after the reopening checks.",{"type":53,"title":54,"steps":55},"steps","Isolate without destroying evidence",[56,59,62,65,68,71],{"title":57,"body":58},"Open the incident log (5 min)","`date -u +%Y-%m-%dT%H:%M:%SZ`\nRecord this time, the reported symptom, the site name, the host and the people involved. Every following line gives the UTC time, the action and its result. The file will be handed to the client, so it stays factual.",{"title":60,"body":61},"Cut public access (10 min)","Serve a maintenance page with HTTP status 503 from the web server or the CDN, or restrict access to the team's IP. Do not rename plugin or theme folders yet: that changes the modification dates you will want to read. If the site is serving malware, cutting access comes before copying.",{"title":63,"body":64},"Copy files and database (15 min)","`mkdir -p ~\u002Fincidents\u002Fclient-a\u002FYYYY-MM-DD`\n`rsync -a \u002Fvar\u002Fwww\u002Fclient-a\u002F ~\u002Fincidents\u002Fclient-a\u002FYYYY-MM-DD\u002Ffiles\u002F`\n`wp db export ~\u002Fincidents\u002Fclient-a\u002FYYYY-MM-DD\u002Fbase.sql --skip-plugins --skip-themes`\n`find ~\u002Fincidents\u002Fclient-a\u002FYYYY-MM-DD -type f -exec sha256sum {} + > ~\u002Fincidents\u002Fclient-a\u002Fhashes-YYYY-MM-DD.txt`\n`chmod -R a-w ~\u002Fincidents\u002Fclient-a\u002FYYYY-MM-DD`\nThe options `--skip-plugins --skip-themes` avoid loading plugins and theme, but the site's code remains suspect. The hashes and the removal of write permissions let you prove later that the copy did not change.",{"title":66,"body":67},"Copy the logs (5 min)","Collect the web server access and error logs and those of the hosting panel. Retention is short on many plans: this is the evidence that disappears fastest, and the one that dates the intrusion.",{"title":69,"body":70},"Close non-essential access (10 min)","Change the panel, SFTP and SSH passwords, remove contractors' access, then replace the salts in `wp-config.php` to invalidate sessions. This rotation is provisional: a backdoor can hand control back to the attacker, so it is repeated after cleaning.",{"title":72,"body":73},"Notify the host (5 min)","On shared hosting, the infection can go beyond the site. Inform support, ask for the logs to be kept and, if the account is suspended, for read access rather than reactivation. Ask whether the server IP appears on a mail blocklist.",{"type":53,"title":75,"steps":76},"Check integrity, command by command",[77,80,83,86,89,92,95],{"title":78,"body":79},"Core files (5 min)","`wp core verify-checksums --include-root`\nCompares installed files with the WordPress.org checksums. A \"File doesn't verify against checksum\" message points to a modified file and \"File should not exist\" to an added one; `--include-root` extends the check to the root directory.",{"title":81,"body":82},"Plugins (5 min)","`wp plugin verify-checksums --all`\nSame principle for plugins hosted on WordPress.org. A premium plugin or a custom theme has no published checksum: compare it with a clean copy from the vendor.",{"title":84,"body":85},"PHP where it does not belong (10 min)","`find wp-content\u002Fuploads -name '*.php'`\n`find . -type f -name '*.php' -mtime -30`\nThe media folder holds no code: any PHP there is suspect. The second command lists PHP files modified in the last thirty days; replace 30 with the estimated age of the intrusion. A modification date can be forged: it guides, it does not prove.",{"title":87,"body":88},"Suspicious code patterns (10 min)","`grep -rEl 'eval\\(|base64_decode\\(|gzinflate\\(' wp-content`\n`eval`, `base64_decode` and `gzinflate` are used to hide code, but legitimate plugins use them too: read every result. A search with no result does not prove the absence of compromise.",{"title":90,"body":91},"Accounts and settings (10 min)","`wp user list --role=administrator --fields=ID,user_login,user_email,user_registered`\n`wp option get siteurl`\n`wp option get home`\n`wp option get users_can_register`\n`wp option get default_role`\nLook for an unknown administrator, a creation date close to the intrusion, a modified `siteurl` or `home` (a frequent cause of redirects), open registration with an overly high default role. This data lives in the `wp_users`, `wp_usermeta` and `wp_options` tables; the prefix may differ.",{"title":93,"body":94},"Scheduled tasks and boot files (10 min)","`wp cron event list`\n`crontab -l`\n`ls -la wp-content\u002Fmu-plugins`\n`find . -name '.htaccess'`\nAn unknown scheduled task can recreate the malicious file every night. Must-use plugins (mu-plugins) load without activation. The `.htaccess` file is often modified: rules reserved for mobile visitors or visitors coming from a search engine explain why the client sees nothing from their desk. There may be others in subfolders.",{"title":96,"body":97},"Database content (10 min)","`wp db search '\u003Cscript' --all-tables-with-prefix`\n`wp option get active_plugins --format=json`\nLook for script tags injected into posts, widgets and options, and reread the list of active plugins: an unknown entry stands out immediately.",{"type":99,"title":100,"body":101},"tip","An empty search proves nothing","Malicious code is often obfuscated, split up or loaded from the database. Checksums only cover core and plugins from the official repository: a custom theme or a file added to the content folder escapes them. Treat these results as clues for the decision that follows, never as a certificate of cleanliness.",{"type":39,"title":103,"body":104},"Date the intrusion before choosing","The client asks \"do we clean or restore?\", and the answer depends on one date: that of the intrusion. Restoring removes the visible symptom, but a backup that already contains the backdoor replays the infection, and restoring without fixing the flaw lets the attacker return the same way. Cleaning by hand often leaves a forgotten file. To date the intrusion, cross at least two sources: the oldest suspicious file, the creation date of the unknown account (`user_registered`), the first abnormal request in the access logs, the installation date of a vulnerable plugin. Keep the oldest date you can prove, then subtract a margin. The date of the last healthy backup is then compared with it.",{"type":106,"title":107,"headers":108,"rows":112},"comparison","Clean, restore or rebuild: the decision table",[109,110,111],"Situation","Decision","Reason",[113,117,121,125,129,133],[114,115,116],"Last healthy backup predates the intrusion, little content written since","Restore, then fix the flaw","The rollback is short and verifiable with the integrity checks",[118,119,120],"Old healthy backup, but orders or content written since (shop, forms)","Restore or rebuild, then bring back recent data from the preserved copy","You recover the useful tables, not the doubtful files; the sorting is validated with the client",[122,123,124],"Unknown intrusion date, or the backup itself is suspect","Rebuild: core and plugins reinstalled clean, content and media imported after sorting","No reliable starting point: only what can be verified is reused",[126,127,128],"A single modified file, known cause, clean copy of core available","Clean by replacing with official files","The scope is bounded and the checksums confirm it",[130,131,132],"Several modified files outside core, unknown accounts, unknown must-use plugin","Rebuild","The number of possible entry points exceeds what a manual review can guarantee",[134,135,136],"Vulnerable plugin still installed, no fix published","Remove or replace it, whichever route is chosen","Otherwise the attacker returns through the same door",{"type":53,"title":138,"steps":139},"Reopen: update, then rotate secrets in order",[140,143,146,149,152,155,158],{"title":141,"body":142},"Update core and plugins","`wp core update`\n`wp plugin update --all`\nDo this on a clean site, after restoring or cleaning. Also remove unused themes and plugins.",{"title":144,"body":145},"Hosting panel, SFTP and SSH","New long, unique passwords kept in a shared vault. Remove unknown keys from `~\u002F.ssh\u002Fauthorized_keys`. These come first: they allow files to be rewritten, so everything else is pointless while they are compromised.",{"title":147,"body":148},"Database","New password for the database user, then update it in `wp-config.php`. The official documentation suggests changing the user name as well.",{"title":150,"body":151},"Salts in wp-config.php","Generate a new set at https:\u002F\u002Fapi.wordpress.org\u002Fsecret-key\u002F1.1\u002Fsalt\u002F and replace the values: open sessions are logged out. Repeat it now: the initial rotation may have been bypassed.",{"title":153,"body":154},"WordPress accounts","Reset the passwords of all administrators and editors, delete unrecognised accounts, enable two-factor authentication and review roles with the command `wp user list --role=administrator`.",{"title":156,"body":157},"API keys and third-party services","Payment, SMTP, CDN, object storage, antispam: regenerate each key at the provider, not only in the site.",{"title":159,"body":160},"Remote management tokens","Multi-site management tools, online backup, monitoring: revoke the old tokens and reconnect the site with new ones.",{"type":43,"title":162,"items":163},"Check before opening to the public",[164,165,166,167,168,169],"**Clean checksums** — `wp core verify-checksums` and `wp plugin verify-checksums --all` no longer return warnings; plugins outside the repository are compared with a fresh vendor copy.","**Accounts accounted for** — the administrator list is reviewed with the client, line by line, and each account is tied to a person.","**No PHP in the media folder** — `find wp-content\u002Fuploads -name '*.php'` returns nothing.","**Complementary scan** — a plugin scanner and a remote scanner, because the official documentation notes that no single tool sees every case.","**Reinforced monitoring for a fixed period** — for example fourteen days, with the integrity checks rerun daily and the login logs read; the end date is written in the incident log.","**A dated healthy backup** — the copy taken right after reopening becomes the new reference point for the next decision.",{"type":53,"title":171,"steps":172},"The message to the client, in three steps and with one voice",[173,176,179,182],{"title":174,"body":175},"The finding, within the hour","The agency writes, not the host or the technical team. Template: \"Your site shows signs of compromise: [observed symptom]. We took it offline at [UTC time] to protect your visitors. The cause is not yet known.\" No assumed cause, no promised deadline.",{"title":177,"body":178},"The action, once isolation is done","Template: \"A full copy has been preserved. We are checking the files, the accounts and the database. By [time], we will tell you whether we clean, restore or rebuild, and why.\" If recent orders are involved, say so: the client decides what they accept to lose.",{"title":180,"body":181},"The follow-up, at reopening","Template: \"The site has been reopened since [date]. Access has been renewed and we are monitoring it for [duration]. Please change the password of your accounts and tell us about any unusual behaviour.\" Only write \"fixed\" for what the checks actually verified.",{"title":183,"body":184},"The incident log, handed to the client","Attach the timestamped log: finding, actions, decision and reason, reopening, monitoring period. It stays factual and assigns no blame.",{"type":39,"title":186,"body":187},"Getting out of the Google warning after reopening","If the browser showed \"Deceptive site ahead\" or the results flagged hacked content, the way out goes through Search Console. The Security issues report lists the affected pages. Google's documentation asks you to request a review only once all issues are fixed on all pages: a premature request can lengthen the delay of the next one, or even get the site classed as a repeat offender. The request describes the problem, the steps taken and the outcome. The announced delay ranges from a few days to several weeks: give the client that range rather than a date. Also fix the flaw: otherwise the site will be infected again.",{"type":43,"title":189,"items":190},"When the output does not say what it seems to say",[191,192,193,194,195],"**\"File should not exist\" at the root** — the file is absent from the official list, so it was added. Not all are hostile (an ownership verification file, for example), but each one must be explained.","**\"File doesn't verify against checksum\" on `readme.html` or `wp-config-sample.php`** — often harmless if the language or version differs. Compare `--locale` and `--version` with what the Updates screen of the dashboard shows before concluding.","**Checksum download fails** — the server may have no outbound access. Do not add `--insecure` on a suspect machine: the option disables certificate validation and exposes you to interception.","**Plugin missing from the check** — it is not in the official repository. Compare its files with a clean vendor copy rather than declaring it healthy.","**The site still redirects after cleaning** — test like a visitor: `curl -sI -A 'Mozilla\u002F5.0 (iPhone)' https:\u002F\u002Fyour-domain.com\u002F` then `curl -sI -e https:\u002F\u002Fwww.google.com\u002F https:\u002F\u002Fyour-domain.com\u002F`. A redirect seen only this way comes from a `.htaccess` rule, a server cache or the CDN: list every `.htaccess` and purge the caches.",{"type":43,"title":197,"items":198},"Going further",[199,200,201],"**A dated backup-plugin incident** — \u002Fblog\u002Fupdraftcentral-cles-volees-backdoors-wordpress-sauvegarde describes a clean restoration in a specific case; this guide gives the general procedure.","**Prove the restoration before you need it** — \u002Fblog\u002Ftester-restauration-sauvegarde-vps-client-agence-procedure explains how to test a backup outside any incident.","**Check the estate every month** — \u002Fblog\u002Fchecklist-audit-securite-mensuel-vps-clients-agence prevents some of the situations described here.",{"type":39,"title":203,"body":204},"Official documentation","WordPress \"My site was hacked\" FAQ: https:\u002F\u002Fwordpress.org\u002Fdocumentation\u002Farticle\u002Ffaq-my-site-was-hacked\u002F\n\nHardening WordPress: https:\u002F\u002Fwordpress.org\u002Fdocumentation\u002Farticle\u002Fhardening-wordpress\u002F\n\nwp core verify-checksums command: https:\u002F\u002Fdeveloper.wordpress.org\u002Fcli\u002Fcommands\u002Fcore\u002Fverify-checksums\u002F\n\nwp plugin verify-checksums command: https:\u002F\u002Fdeveloper.wordpress.org\u002Fcli\u002Fcommands\u002Fplugin\u002Fverify-checksums\u002F\n\nwp db search command: https:\u002F\u002Fdeveloper.wordpress.org\u002Fcli\u002Fcommands\u002Fdb\u002Fsearch\u002F\n\nGoogle Search Console Security issues report: https:\u002F\u002Fsupport.google.com\u002Fwebmasters\u002Fanswer\u002F9044101","Restore one client's site without touching the others","On an infrastructure where each account is isolated and backups are daily on all Agency plans, restoring one site does not touch the others. With ServOrbit.com, the client keeps calling you, and restoring one account happens on infrastructure that is already partitioned.","Discover the agency plan","\u002Fsolutions\u002Fagences",[210,227,243],{"id":211,"slug":212,"slugs":213,"title":217,"excerpt":218,"readTime":219,"views":18,"isPinned":19,"publishedAt":220,"updatedAt":221,"category":222,"categories":223,"featuredImage":30,"bgImage":31,"posterImage":225,"relatedSolution":226},535,"updraftcentral-stolen-keys-wordpress-backdoors-clean-restore",{"fr":214,"en":212,"ar":215,"es":216},"updraftcentral-cles-volees-backdoors-wordpress-sauvegarde","updraftcentral-مفاتيح-مسروقة-أبواب-خلفية-ووردبريس-استعادة-نظيفة","updraftcentral-claves-robadas-puertas-traseras-wordpress-restaurar","UpdraftCentral: stolen keys, fake plugins, a clean restore","Stolen UpdraftCentral keys led to fake plugins on WordPress sites: revoke the keys, audit plugins and admin accounts, then restore from a clean backup.",9,"2026-10-30T00:00:00+00:00","2026-10-31T01:54:05+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[224],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fupdraftcentral-cles-volees-backdoors-wordpress-sauvegarde-poster.svg",{"categorySlug":34,"appSlug":35},{"id":228,"slug":229,"slugs":230,"title":234,"excerpt":235,"readTime":236,"views":18,"isPinned":19,"publishedAt":237,"updatedAt":238,"category":239,"categories":240,"featuredImage":30,"bgImage":31,"posterImage":242,"relatedSolution":30},523,"test-client-vps-backup-restore-agency-procedure",{"fr":231,"en":229,"ar":232,"es":233},"tester-restauration-sauvegarde-vps-client-agence-procedure","اختبار-استعادة-نسخة-احتياطية-vps-عميل-وكالة-إجراء","probar-restauracion-copia-seguridad-vps-cliente-agencia-procedimiento","Testing a client backup restore: an agency procedure","A quarterly procedure to restore a client site on a disposable VPS, measure the actual RTO and RPO, and hand the client a dated restore test log.",11,"2026-10-28T00:00:00+00:00","2026-10-29T03:10:23+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[241],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Ftester-restauration-sauvegarde-vps-client-agence-procedure-poster.svg",{"id":244,"slug":245,"slugs":246,"title":250,"excerpt":251,"readTime":252,"views":18,"isPinned":19,"publishedAt":237,"updatedAt":253,"category":254,"categories":255,"featuredImage":30,"bgImage":31,"posterImage":257,"relatedSolution":30},521,"monthly-security-audit-checklist-client-vps-agency",{"fr":247,"en":245,"ar":248,"es":249},"checklist-audit-securite-mensuel-vps-clients-agence","قائمة-تدقيق-أمان-vps-شهرية-للعملاء-للوكالات","auditoria-seguridad-mensual-vps-clientes-agencia-checklist","Monthly client VPS security audit: a 45-minute checklist","Seven checks to run every month on a client VPS: updates, kernel, SSH accounts, ports, certificates, backups, logs. Includes a dated one-page report.",10,"2026-10-29T03:10:22+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[256],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fchecklist-audit-securite-mensuel-vps-clients-agence-poster.svg",1793480312432]