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; }
- Apache (.htaccess in uploads):
- 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