Securing Your Self-Hosted Site: Firewall, Login Limits, File Permissions, and Malware Scanning
1. Firewall: Add WAF to Your Cloud Security Group and Close Unnecessary Ports First
Many people think a firewall is software installed on the server. But for a self-hosted site on a cloud host, the first line of defense is the cloud provider’s security group. Leaving ports like 3306, 6379, and 9000 wide open by default is the biggest risk. Your database and Redis are directly exposed to the public internet, and a scanner can brute-force its way in within minutes. The right approach: allow only ports 80 and 443 in your security group, and restrict port 22 to your company’s fixed outbound IP.
The second line of defense is a WAF (Web Application Firewall). Cloudflare’s free plan is enough. It automatically blocks SQL injection, XSS, and common scanners. Don’t set WAF rules too aggressively, or you’ll block legitimate visitors. Start in log-only mode for 3 days, check that the false positive rate is under 1%, then switch to blocking mode.
| Port / Service | Default state | Status After Hardening | Why |
|---|---|---|---|
| 80/443 | Open to 0.0.0.0/0 | Open to 0.0.0.0/0 | Must be publicly accessible |
| 22 SSH | Open to 0.0.0.0/0 | Restrict to company fixed IP, switch to key-based login | Main target for password brute-forcing |
| 3306 MySQL | Often opened by mistake | Allow only localhost 127.0.0.1 | Never expose to the public internet |
| 6379 Redis | Often opened by mistake | Allow only localhost, set a password | Unauthorized access lets attackers write public keys |
| 9000 PHP-FPM | Local socket | Keep it on a local socket, don’t open TCP | No public access needed |
| 8888/BT Panel | Often exposed | Restrict by IP + change default port | Panels are a high-frequency attack target |
Example: A WordPress site using the BT Panel had all ports open in its security group when it first went live. Three days later, server CPU hit 100%. After logging in, we found port 3306 was being brute-forced by overseas IPs, and the MySQL log was full of failed connection attempts. We changed the security group to allow only 80/443 and restricted port 22 to the company IP. CPU dropped back to 5% immediately. This incident could have been avoided in 10 minutes, but most people don’t even look at their security group when going live.
How to configure WAF rules follows the same logic as contact form spam protection: log and observe first, then block. Don’t block every suspicious request right out of the gate.
2. Login Restrictions: Narrow the Brute-Force Entry Point
WordPress’s wp-admin is the number one way into a hacked standalone site. With the default admin username, weak passwords, and a publicly reachable login page, a scanner can run through an entire dictionary in a few hours. Counterintuitively, installing a login restriction plugin works better than buying any security scanning product, because the vast majority of brute-force attacks never even reach the exploit stage — they get blocked at the login page.
The login restriction trio: lock the account for 30 minutes after 5 failed attempts, change the default login path (swap /wp-admin for a random string), and enforce 2FA. Passwords should be at least 16 characters, generated by a password manager — never a word plus numbers. The same goes for SSH: disable password login and allow keys only.
| Setting | Default | Hardening Setting | Tool / Implementation |
|---|---|---|---|
| Failed login lockout | Unlimited | Lock for 30 minutes after 5 failures | Wordfence or custom fail2ban |
| Login path | /wp-admin | Random string like /login-7f3k | WPS Hide Login plugin |
| Two-factor authentication | None | Enforce TOTP 2FA | Wordfence or Two Factor |
| Password length | 6 characters | ≥16 random characters | Generated by a password manager |
| SSH authentication | Password login | Keys only, passwords disabled | Set PasswordAuthentication no in sshd_config |
| Database user | Remote root | Dedicated user, localhost only | Create a separate MySQL account |
Example scenario: an overseas SaaS site has its admin login page at /secret-admin, 2FA enabled, and a 30-minute lockout after 5 failures. Monitoring logs show roughly 2,000 login attempts per week from overseas IPs, all locked out, not one successful. Without restrictions, at average password strength, the odds of a successful credential-stuffing attack within 3 months would exceed 30%.
For the overall approach to plugin selection and tech stack, see crater tech stack’s four in-house plugins.
3. File Permissions: Stop Uploaded Malicious Files from Running
File permissions are a layer many people overlook. After WordPress is hacked, attackers typically upload a PHP webshell to the wp-content/uploads directory. If uploads allows PHP execution, that webshell can take over the whole site immediately; if uploads forbids PHP execution, the attacker uploads it and it just sits there, useless.
The standard permissions are: directories 755, files 644, and wp-config.php set to 400 (owner read-only). Don’t take the lazy route and set everything to 777 — that’s like hanging your keys on the door. Add a rule to the nginx config for the uploads directory to block execution of .php, .phtml, .php5 and similar files.
| Path | Permission | Owner | Description |
|---|---|---|---|
| Site root | 755 | www-data:www-data | Never use 777 |
| wp-config.php | 400 | root | Web process can’t read it |
| wp-content/themes | 755 | www-data | Auto-updates need write access |
| wp-content/uploads | 755 | www-data | PHP execution disabled |
| wp-content/plugins | 755 | www-data | Plugin updates need write access |
| .git directory | Don’t expose it on the web | — | Exposure leaks source code |
Another common reason sites get hacked: database backups sitting right in the web directory. Many backup plugins store .sql.gz files in wp-content/backup/ by default, and attackers who find them just download the whole site’s data. Do it right: keep backups outside the web root, or upload them automatically to object storage and delete the local copy. For backup strategy, see Backup and recovery drills for your site.
4. Malicious scanning and patching: scan weekly, patch within 7 days
Malicious scanning isn’t something you do after you get hacked. Run a core file hash comparison once a week. If your WordPress core files don’t match the hashes for a known version, they’ve been tampered with. Plugin and theme updates need to be done within 7 days. The average window from public disclosure of a vulnerability to exploitation by automated scanners is 14 days, so patching within 7 days avoids most attacks.
There are three types of scanning tools: plugin scanners like Wordfence can detect known webshell signatures; ClamAV runs server-level virus scans; server log analysis can surface unusual access patterns. Use all three together. Don’t expect any single tool to cover everything. For how to analyze AI crawler logs, see AI Crawler Log Analysis. The same log analysis methods can be used to spot malicious scanners.
| Checklist | Frequency | Tools | Exception handling |
|---|---|---|---|
| Core file hash comparison | Weekly | Wordfence core integrity | If differences appear, restore from a clean version |
| Plugin and theme updates | Every Monday | WP admin + logs | Must be updated within 7 days |
| webshell scanning | Weekly | ClamAV or Wordfence | Quarantine and delete malicious files |
| Suspicious login records | Daily | fail2ban logs | Automatically ban attacking IPs |
| Recently modified files in the web directory | Weekly | find -mtime -7 | Investigate unexpected changes immediately |
| Abnormal database writes | Weekly | base64 content in wp_posts | Clean up hidden eval code |
Counterintuitive reminder: don’t rush to clean up online after you’ve been hacked. The biggest problem with online cleanup is that you can’t fully root out webshells and cron jobs hidden in the database, and two weeks later it comes back. The right process is: take the site offline, restore from the most recent clean backup, change all passwords and keys, check for remnants in cron and the database, then bring the site back online. For the full post-hack process, see Cleanup and re-indexing after a site is hacked.
For the daily inspection checklist, see Six Monthly Tasks for Site Maintenance, and build security hardening into it. For how to set alert thresholds, see Site monitoring and alerts.
FAQ
Your site just got hacked. What should you do first?
Should I buy a paid WAF, or is Cloudflare’s free plan enough?
How often should WordPress plugins be updated, and is auto-update safe?
How should I set permissions for wp-config.php and the uploads directory?
Should I change the default SSH port 22, and can I keep password login?
How do I tell if my site has been hacked, and are there warning signs?
Final thoughts
The most expensive part of hardening security isn’t the tools — it’s the “it won’t happen to me” mindset. Your site isn’t a bank, but it is where your leads come in. If it gets injected with malware or goes down, you lose real revenue. You don’t have to roll out all four layers at once, but do them in order: close the ports, restrict logins, fix file permissions, then add scanning. The first three take an afternoon and deliver the highest ROI.
Three steps to get it done: First, open your cloud security group and close ports like 3306, 6379, and 8888 that should never be public, and restrict SSH to your office IP. Second, change the wp-admin path, turn on 2FA, lock accounts after 5 failed attempts, and switch SSH to key-based login. Third, run a core file hash check and plugin updates on a fixed schedule every week. Do these three things and you’ve already blocked 80% of common attacks.
One line worth remembering: sites rarely get hacked by brilliant attackers — it’s usually weak passwords, outdated plugins, and exposed database backups. Closing off those three entry points beats buying any security product.
