Helping a Friend Recover a Hacked Website: The Safe Cleanup Playbook

Story: Today a friend pinged me in a panic: his website was hacked and malware kept showing up even after a restore. I gave him a few quick steps to contain the damage, then realized it’s worth writing steps down properly so I can share with others next time. Here’s the practical, safe sequence I recommend in this situation.

1) Contain the Incident (Right Now)

  • Take the site off public access. Maintenance page or basic auth. Protects visitors and stops further damage.
  • Make two backups (files + database): one for reference, one as a safety net. Don’t reuse them in production yet.
  • Rotate all passwords and keys: hosting/SSH/FTP, database, CMS admins, email/SMTP, and any API keys. Remove unused accounts.
  • Scan your own laptop/PC for malware to avoid re-infection through saved credentials.

2) Rebuild on a Clean Base (Safest Path)

Cleaning “in place” often misses hidden backdoors. A fresh rebuild is safer and often faster.

  • Fresh CMS core: download the latest from the official source.
  • Plugins/extensions: reinstall only what you truly need, from trusted official sources.
  • Theme:
    • If it’s a standard theme, download a fresh copy.
    • If it’s custom, copy it locally, scan it, review unusual files/folders, and only move it over once you’re confident it’s clean.
  • Media (uploads) folder: copy locally and ensure it contains only media files (images, PDFs, videos). Delete any executables (e.g., .php, .phtml, scripts).
  • Database:
    • If you have a pre-infection backup, use that. Otherwise, restore the latest and inspect: unknown admin users, strange options, injected scripts in posts/widgets.
    • Remove rogue accounts; reset all user passwords.
  • Deploy the clean code (ideally via Git so changes are traceable). Test thoroughly.
  • Switch traffic to the clean environment once satisfied. Keep the old one offline for a few days as reference.

3) Why Simple Restores Often Fail

  • Backups were already infected.
  • Persistence mechanisms (cron jobs, must-use plugins, DB payloads, webshells) re-download malware.
  • Stolen credentials or infected local machines keep reopening the door.

4) Hardening Before and After Go-Live

  • Update everything (core, plugins, themes). Remove what you don’t use.
  • Least privilege: minimal roles for users; DB user with only needed rights.
  • Disable file editing in admin (WordPress): define('DISALLOW_FILE_EDIT', true);
  • Block PHP execution inside uploads:
    • Apache (.htaccess in uploads):
      • <FilesMatch "\.(php|php\.)"> Deny from all </FilesMatch>
    • Nginx (server block):
      • location ~* /uploads/.*\.(php|php\.) { return 403; }
  • Rotate salts/keys and all API tokens.
  • Enable 2FA on hosting, CMS.
  • Git-based deployments: In my experience—especially among my developer friends in Asia—Git-based deployment is still underused. It’s time to switch. Manual uploads and file-manager edits hide changes, make rollbacks painful, and can quietly reintroduce malware. With Git, every change is tracked, reviewed, and deployed from a clean build, which is safer and easier to maintain

5) Quick Checklist

  • Site offline / maintenance mode
  • Two backups taken (files + DB)
  • All passwords/keys rotated; unused users removed
  • Fresh CMS core installed
  • Only trusted plugins re-installed
  • Theme replaced or deeply scanned
  • Uploads cleaned (media only; no executables)
  • DB checked for rogue admins/options/injections
  • Git deploys set up
  • WAF, 2FA, backups, and updates enabled
  • PHP blocked in uploads; file editing disabled
  • Salts/keys/API tokens rotated

6) When to Call a Pro

  • E-commerce or sensitive data involved
  • Malware returns after a clean rebuild
  • Signs of deeper server compromise (spam relays, outbound attacks)

Final word: Don’t just delete suspicious files—contain, rebuild cleanly, and harden so it doesn’t happen again


By Shakir Ali — 15+ years in system administration. Updated: 30 Aug 2025



Leave a Reply

Your email address will not be published. Required fields are marked *