Server-Level Hardening
Server-level security is your foundation. A misconfigured server undermines every other security measure. These steps apply primarily to VPS and dedicated servers, though shared hosting users should verify their provider implements these at the infrastructure level.
This checklist is compiled from 6+ years of server hardening across shared hosting, managed VPS, and bare-metal environments, incorporating CIS benchmarks, OWASP guidelines, and real-world incident response.
Operating System Hardening
- 1. Keep the OS updated — Enable unattended-upgrades (Ubuntu/Debian) or dnf-automatic (RHEL/Fedora) for automatic security patches. On Ubuntu:
sudo apt install unattended-upgrades && sudo dpkg-reconfigure -plow unattended-upgrades. Critical vulnerabilities are often exploited within 24-48 hours of disclosure — automatic updates close this window. - 2. Remove unnecessary packages — Every installed package is a potential attack surface. Remove unused services:
sudo apt autoremove. Audit installed packages withdpkg --listand remove anything you don't recognize or need. - 3. Disable root login via SSH — Edit
/etc/ssh/sshd_config: setPermitRootLogin no. Create a regular user with sudo privileges instead. Root login gives attackers full system access with a single credential. - 4. Use SSH key authentication only — Disable password authentication:
PasswordAuthentication noin sshd_config. Generate a 4096-bit RSA or ED25519 key pair. SSH keys are immune to brute-force password attacks, which account for 40%+ of server compromise attempts. - 5. Change the default SSH port — Move SSH from port 22 to a non-standard port (e.g., 2222, 49152-65535). This eliminates 95% of automated scanning bots. Not security through obscurity — it's reducing noise to focus on real threats.
- 6. Set proper file permissions — Web files: 644 for files, 755 for directories. wp-config.php: 600 or 640. Never use 777 permissions — it allows any user on the server to read, write, and execute your files.
Web Server Hardening
- 7. Hide server version information — Nginx:
server_tokens off;. Apache:ServerSignature OffandServerTokens Prod. Version disclosure helps attackers target known vulnerabilities in specific software versions. - 8. Disable directory listing — Nginx:
autoindex off;(default). Apache:Options -Indexes. Directory listing exposes your file structure, backup files, and potentially sensitive data. - 9. Set security headers — Add these headers to every response:
X-Content-Type-Options: nosniff,X-Frame-Options: SAMEORIGIN,X-XSS-Protection: 1; mode=block,Strict-Transport-Security: max-age=31536000; includeSubDomains,Content-Security-Policy(tailored to your application). These prevent clickjacking, XSS, and MIME-type attacks. - 10. Limit request size and rate — Nginx:
client_max_body_size 10m;andlimit_req_zonefor rate limiting. This prevents large file upload attacks and application-layer DDoS.
PHP Hardening (for PHP-based Sites)
- 11. Disable dangerous PHP functions — In php.ini:
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec. These functions allow arbitrary command execution if an attacker gains code execution through a vulnerability. - 12. Set open_basedir — Restrict PHP to your web directory:
open_basedir = /var/www/html:/tmp. This prevents a compromised PHP script from reading files outside your web root, like /etc/passwd. - 13. Disable remote file inclusion —
allow_url_fopen = Offandallow_url_include = Offin php.ini. This blocks attackers from including malicious PHP files hosted on external servers. - 14. Hide PHP version —
expose_php = Offin php.ini. Like server version disclosure, this helps attackers target version-specific vulnerabilities.
WAF & Firewall Configuration
System Firewall (iptables/UFW)
- 15. Enable and configure UFW — On Ubuntu/Debian:
sudo ufw default deny incoming && sudo ufw default allow outgoing && sudo ufw allow ssh && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw enable. This whitelists only essential ports and blocks everything else. - 16. Block common attack ports — Explicitly deny ports used by vulnerable services: MySQL (3306), PostgreSQL (5432), Redis (6379), Memcached (11211). These should never be exposed to the internet — use SSH tunneling or private networking for database access.
- 17. Enable connection rate limiting — UFW:
sudo ufw limit sshlimits SSH connections to 6 per 30 seconds per IP. For Nginx, uselimit_conn_zoneandlimit_req_zoneto prevent connection flooding. - 18. Install and configure fail2ban — Fail2ban monitors log files and bans IPs that show malicious patterns. Install:
sudo apt install fail2ban. Configure jails for SSH, Nginx, and your application. Default: ban for 10 minutes after 5 failed attempts. For SSH, increase to 1-hour bans after 3 attempts. Fail2ban blocks 99% of brute-force attempts. - 19. Set up IP whitelisting for admin panels — Restrict access to /wp-admin, /administrator, phpMyAdmin, and other admin interfaces to your IP addresses only. In Nginx:
location /wp-admin { allow YOUR.IP; deny all; }. This makes admin interfaces invisible to attackers.
Web Application Firewall (WAF)
- 20. Deploy a WAF layer — Options by hosting type:
Shared hosting: Use host-provided WAF (Imunify360 on ChemiCloud, AI anti-bot on SiteGround) or Cloudflare free tier
VPS: ModSecurity with OWASP Core Rule Set, or Cloudflare Pro ($20/mo)
Managed VPS: Cloudways includes server-level firewall + optional Cloudflare Enterprise ($4.99/mo)
A WAF blocks SQL injection, XSS, file inclusion, and other OWASP Top 10 attacks at the edge before they reach your application. - 21. Configure WAF rules for your application — Default WAF rules cause false positives with WordPress admin, WooCommerce checkout, and page builders. Whitelist legitimate admin actions, AJAX endpoints, and REST API routes. Test in "detection only" mode for 1 week before switching to blocking mode.
- 22. Enable bot protection — Configure your WAF to challenge or block known bad bots. Cloudflare Bot Management, SiteGround AI anti-bot, and Imunify360 all provide bot classification. Allow search engine bots (Googlebot, Bingbot) while blocking scrapers, spam bots, and vulnerability scanners.
- 23. Set up geo-blocking (if applicable) — If your audience is US-only, blocking traffic from countries that generate the most attacks (based on your logs) reduces attack surface significantly. Cloudflare, UFW with GeoIP, or Nginx with the GeoIP2 module all support country-based blocking.
DDoS Mitigation
- 24. Enable Cloudflare as reverse proxy — Even the free tier provides basic L3/L4 DDoS mitigation and hides your origin IP. Enable "Under Attack Mode" during active attacks. For business-critical sites, Cloudflare Pro ($20/mo) adds WAF rules and advanced DDoS filtering.
- 25. Configure SYN flood protection — On Linux:
net.ipv4.tcp_syncookies = 1,net.ipv4.tcp_max_syn_backlog = 2048,net.ipv4.tcp_synack_retries = 2in /etc/sysctl.conf. These kernel parameters protect against the most common network-layer attack.
Authentication & Access Control
Two-Factor Authentication (2FA)
- 26. Enable 2FA on your hosting control panel — cPanel, Plesk, SPanel, and all major managed dashboards (Cloudways, Kinsta, Hostinger) support TOTP-based 2FA. Use an authenticator app (Google Authenticator, Authy, 1Password) — never SMS-based 2FA, which is vulnerable to SIM-swapping attacks.
- 27. Enable 2FA on your CMS admin — WordPress: install Wordfence or WP 2FA plugin. Apply 2FA to all admin and editor accounts. 81% of WordPress breaches involve compromised admin credentials — 2FA eliminates this attack vector entirely.
- 28. Enable 2FA on your domain registrar — Your domain is the master key to everything. If an attacker compromises your registrar account, they can redirect your DNS to a phishing site, intercept email (including password resets), and effectively own your entire web presence. Enable 2FA and registrar lock on every domain.
- 29. Enable 2FA on DNS management — Cloudflare, Route 53, or wherever you manage DNS records. Unauthorized DNS changes can redirect traffic, intercept email, and bypass every other security measure.
Password & Access Policies
- 30. Use unique, strong passwords everywhere — Minimum 16 characters, randomly generated, stored in a password manager (1Password, Bitwarden). Every account — hosting, CMS, database, FTP, email — gets a unique password. A single reused password compromises everything.
- 31. Remove default admin accounts — WordPress: never use "admin" as a username. cPanel: change the default account name. Database: remove the default root@localhost remote access. Default usernames are the first thing attackers try.
- 32. Implement the principle of least privilege — Every user account should have the minimum permissions needed. WordPress contributors don't need admin access. Database users should only have SELECT/INSERT/UPDATE/DELETE on their specific database — never GRANT or CREATE privileges.
- 33. Audit and revoke inactive access — Monthly review: remove former team members' hosting accounts, revoke unused SFTP credentials, delete inactive WordPress users. Every abandoned account is a potential entry point — especially for agencies and teams.
- 34. Disable FTP — use SFTP only — FTP transmits credentials in plaintext. Disable FTP entirely and use SFTP (SSH File Transfer Protocol) for all file transfers. In your SSH config:
Subsystem sftp /usr/lib/openssh/sftp-serverand disable plain FTP in your firewall.
Database Security
- 35. Change the default database prefix — WordPress default is
wp_. Change to a random prefix during installation (e.g.,x7k9_). This breaks automated SQL injection scripts that target the default table names. - 36. Restrict database access to localhost — In MySQL: ensure remote root access is disabled. The database should only accept connections from 127.0.0.1, not from any external IP. Use SSH tunneling for remote database management.
- 37. Use separate database users per application — Each website or application gets its own database user with access only to its own database. If one site is compromised, the attacker cannot access other databases on the same server.
SSL/TLS & Encryption
SSL Certificate Configuration
- 38. Install SSL on every domain and subdomain — Free certificates from Let's Encrypt (auto-renewing via Certbot) cover most use cases. For e-commerce or high-trust sites, consider Organization Validation (OV) or Extended Validation (EV) certificates. Ensure staging, API, and mail subdomains all have valid SSL.
- 39. Force HTTPS everywhere — Redirect all HTTP traffic to HTTPS. Nginx:
return 301 https://$server_name$request_uri;in the port 80 server block. Apache:RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]. AddStrict-Transport-Securityheader to prevent downgrade attacks. - 40. Configure TLS 1.2+ only — Disable TLS 1.0 and 1.1 (deprecated, known vulnerabilities). Nginx:
ssl_protocols TLSv1.2 TLSv1.3;. Use strong cipher suites:ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384'; - 41. Enable OCSP stapling — OCSP stapling speeds up SSL handshakes and improves privacy by eliminating the browser's need to contact the certificate authority for revocation status. Nginx:
ssl_stapling on; ssl_stapling_verify on; - 42. Set up automatic certificate renewal — Let's Encrypt certificates expire every 90 days. Verify Certbot's auto-renewal cron is active:
sudo certbot renew --dry-run. An expired SSL certificate breaks your site and destroys user trust instantly.
Encryption at Rest
- 43. Encrypt database backups — Use GPG or openssl to encrypt backups before off-site transfer:
mysqldump dbname | gzip | gpg --encrypt -r your@email.com > backup.sql.gz.gpg. Unencrypted backups stored in cloud storage are a data breach waiting to happen. - 44. Enable disk encryption on VPS — Most cloud providers offer encrypted volumes (DigitalOcean, AWS EBS encryption, GCP persistent disk encryption). Enable encryption for volumes containing sensitive data. This protects against physical server access and improper disk disposal.
Backups & Malware Scanning
Backup Strategy
- 45. Implement the 3-2-1 backup rule — Maintain 3 copies of your data, on 2 different storage types, with 1 off-site. Copy 1: hosting provider's automatic daily backup. Copy 2: WordPress backup plugin (UpdraftPlus, BlogVault) to cloud storage (S3, Google Drive). Copy 3: monthly downloaded full backup to local storage. This protects against host failure, account compromise, and provider shutdown.
- 46. Test backup restores quarterly — A backup is worthless if it can't be restored. Every 3 months: create a staging environment, restore from your most recent backup, verify pages load, forms submit, and database content is intact. Document the restore process for emergencies when you're stressed and possibly not thinking clearly.
- 47. Set retention to 30+ days — Malware can go undetected for weeks. If your newest clean backup is 7 days old and malware was injected 10 days ago, you've lost your recovery point. 30-day retention provides safety margin for slow-developing threats. ChemiCloud (30 days) and A2 Hosting Server Rewind (30 days) offer the best retention.
Malware Detection & Response
- Continuous scanning — Shared hosting: use host-provided scanning (Imunify360 on ChemiCloud, SShield on ScalaHosting). VPS: install ClamAV + maldet (Linux Malware Detect) for scheduled scans. WordPress: Wordfence or Sucuri scanner. Run full scans weekly minimum, real-time file monitoring daily.
- File integrity monitoring — Monitor critical files for unauthorized changes. WordPress: Wordfence file comparison detects modified core files, plugin files, and theme files. VPS: AIDE (Advanced Intrusion Detection Environment) or OSSEC for system-level file integrity monitoring. Any unexpected change is a potential compromise indicator.
- Malware response playbook — When malware is detected: 1) Isolate — take the site offline or enable maintenance mode. 2) Assess — determine the scope of compromise (which files, database entries). 3) Clean — remove malicious files, restore from clean backup if needed. 4) Patch — fix the vulnerability that allowed the infection. 5) Monitor — increase scan frequency for 30 days post-incident. 6) Report — document the incident for future reference.
- Log monitoring and alerting — Review access logs and error logs weekly. Set up alerts for: failed login attempts (>5 per hour), 500 errors (potential exploit attempts), new file creation in upload directories, and unusual outbound connections. Use Fail2ban for automated response to log-based threats.
Security Audit Schedule
Implement a recurring security audit cycle:
- Daily: Automated malware scan, backup verification, uptime monitoring
- Weekly: Log review, failed login analysis, plugin/theme update check
- Monthly: Access audit (revoke unused credentials), firewall rule review, SSL certificate expiry check
- Quarterly: Full backup restore test, vulnerability scan (WPScan, Nmap), security header verification
- Annually: Complete security audit, password rotation, review and update this entire checklist
FAQ
Frequently Asked Questions
Do I need to worry about server security on shared hosting?
The hosting provider handles server-level security on shared hosting, but you're still responsible for application-level security: strong passwords, 2FA on your CMS and hosting panel, keeping WordPress/plugins updated, and using security plugins. About 60% of shared hosting compromises come from weak application-level security, not server-level failures.
What's the single most impactful security step I can take?
Enable two-factor authentication (2FA) on every account — hosting panel, WordPress admin, domain registrar, and email. This single step blocks 81% of credential-based attacks. Combined with strong unique passwords in a password manager, you eliminate the most common attack vector entirely.
Is Cloudflare free tier enough for security?
For basic sites, yes. Cloudflare free provides DNS-level DDoS protection, SSL, and hides your origin IP. However, it lacks the Web Application Firewall (WAF) rules that block SQL injection and XSS — those require Cloudflare Pro ($20/mo). For WordPress sites handling sensitive data or e-commerce, upgrade to Pro or use a host with built-in WAF (ChemiCloud's Imunify360, Cloudways + CF Enterprise).
How often should I update WordPress plugins and themes?
Apply security updates within 24 hours of release — vulnerabilities are actively exploited within hours of public disclosure. Feature updates can wait 3-7 days to ensure stability. Enable auto-updates for security-critical plugins (Wordfence, Sucuri) and WordPress core. Always backup before major updates and test on staging if available.
What should I do immediately if my site gets hacked?
1) Take the site offline (maintenance mode or DNS redirect). 2) Change all passwords — hosting, CMS, database, FTP, email. 3) Scan for malware using your host's tools or Sucuri SiteCheck. 4) Restore from the most recent clean backup (verify the backup pre-dates the compromise). 5) Update all software and patch the vulnerability. 6) Enable 2FA on everything. 7) Monitor closely for 30 days.
Do managed hosting providers handle all security for me?
Managed providers handle server-level security: OS updates, firewall, network protection, and often malware scanning. You're still responsible for application-level security: CMS updates, plugin security, strong passwords, 2FA, and content-level threats. Think of it as shared responsibility — they secure the infrastructure, you secure the application running on it.
The Bottom Line
Best Security on Shared Hosting
Best Managed Security
Best Security Value
Security is not a product — it's a process. Start with the highest-impact items: 2FA everywhere, automatic updates, and tested backups. For shared hosting, ChemiCloud ($2.49/mo) provides the most comprehensive built-in security. For VPS, Cloudways ($14/mo) handles the heavy lifting. Use this checklist as a recurring audit tool — review quarterly and update annually.
More guides: Best DDoS Protection Hosting 2026 • Best Malware Scanning Hosting 2026 • Managed vs Unmanaged VPS 2026