You just clicked “Deploy”, received a root password by email, and now you own a server that is already being scanned by bots. That is not an exaggeration: a fresh IP address typically starts receiving automated SSH login attempts within minutes of going live.
This guide shows you how to secure a web hosting server in the first 48 hours, even if you have never administered a Linux box before. Every step includes the exact command to paste and the control panel equivalent (cPanel/WHM, Plesk, or a provider dashboard) so you can follow along whether you are on a bare VPS or a managed hosting account.
Who this checklist is for
- You bought a VPS or cloud server (Ubuntu 24.04/26.04 LTS, Debian 12/13, AlmaLinux 9/10) and you have root access.
- You are on shared or managed hosting and can only act through cPanel, Plesk or DirectAdmin. Skip the shell commands and use the “Control panel equivalent” boxes.
- You want a repeatable process, not a 60-page NIST document.
Rule of thumb: do steps 1 to 6 before you install WordPress, Laravel, Node or anything else. Hardening an empty server takes 45 minutes. Cleaning a compromised one takes days.
The 48-hour hardening checklist at a glance
| # | Setting to change | Time | Risk if skipped |
|---|---|---|---|
| 1 | Patch the whole system | 5 min | Known exploits, public CVEs |
| 2 | Create a non-root sudo user | 3 min | One leaked password = total loss |
| 3 | SSH key authentication | 10 min | Brute force succeeds eventually |
| 4 | Disable root login and password auth | 5 min | 99% of bot traffic targets root |
| 5 | Firewall: close unused ports | 10 min | Exposed DB, Redis, Docker APIs |
| 6 | fail2ban / brute force protection | 10 min | Credential stuffing, CPU drain |
| 7 | Automatic security updates | 5 min | Unpatched drift after month 2 |
| 8 | File ownership and permissions | 15 min | Web shells, cross-site file reads |
| 9 | TLS certificate and forced HTTPS | 10 min | Session and password interception |
| 10 | Lock down the database | 10 min | Data theft, ransom notes in tables |
| 11 | Off-server backups + restore test | 20 min | No recovery path |
| 12 | Logging, alerts and a WAF | 20 min | Breaches go unnoticed for months |

Step 1: Patch the system before anything else
Server images are built once and reused for weeks. Your “brand new” server may be missing dozens of security patches.
Debian / Ubuntu
apt update && apt upgrade -y
apt autoremove -y
reboot
AlmaLinux / Rocky / RHEL
dnf upgrade --refresh -y
reboot
Control panel equivalent
- WHM: Home > Software > Update System Software, then Update Server Software.
- Plesk: Tools & Settings > Updates and Upgrades.
- Shared hosting: nothing to do, but check that your PHP version is still supported (PHP 8.2 or newer in 2026). Old PHP branches stop receiving security fixes.
Step 2: Create a non-root user with sudo
Working as root all day means every typo and every compromised process runs with unlimited power. Create a personal account instead.
# Debian / Ubuntu
adduser deploy
usermod -aG sudo deploy
# AlmaLinux / Rocky
adduser deploy
passwd deploy
usermod -aG wheel deploy
Open a second terminal window and confirm you can log in as deploy and run sudo -v before you touch the SSH config. Never close your working session until the new access is proven. sitepoint.com has covered this at length.
Control panel equivalent
In cPanel/Plesk, never use the main admin account for daily FTP or file edits. Create a separate user per site, and enable two-factor authentication on the panel login (cPanel: Security > Two-Factor Authentication, Plesk: My Profile > Enable 2FA).
Step 3: Set up SSH key authentication
A 2048-bit or stronger key cannot be guessed. A password can. Generate the key on your own computer, not on the server.
# On your laptop (macOS, Linux, Windows PowerShell)
ssh-keygen -t ed25519 -C "laptop-2026"
# Copy it to the server
ssh-copy-id deploy@YOUR_SERVER_IP
# Or manually, if ssh-copy-id is unavailable
cat ~/.ssh/id_ed25519.pub | ssh deploy@YOUR_SERVER_IP \
"mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Set a passphrase on the key. If your laptop is stolen, the passphrase is the only thing standing between the thief and your server.
Control panel equivalent
- cPanel: Security > SSH Access > Manage SSH Keys > Import Key, then click Authorize.
- Plesk: Tools & Settings > SSH Keys Manager (or the SSH Terminal extension).
- Provider dashboards (Hetzner, DigitalOcean, OVH, Vultr): add the key during creation so the server never has a root password at all.

Step 4: Disable root login and password authentication
This single change removes the majority of automated attacks against your server. Edit the SSH daemon configuration:
sudo nano /etc/ssh/sshd_config
Set or add these directives:
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
X11Forwarding no
MaxAuthTries 3
AllowUsers deploy
On Ubuntu 24.04 and newer, cloud images often place overrides in /etc/ssh/sshd_config.d/. Check that folder too, or your changes will be silently ignored. It is the sort of thing a careful digital marketing team handles once and never revisits.
sudo sshd -t # test the syntax, must return nothing
sudo systemctl restart ssh # or: systemctl restart sshd
Should you change the SSH port?
Moving SSH from 22 to something like 2222 is obscurity, not security, but it does cut log noise dramatically. It is optional. If you do it, update the firewall first, and remember that ports below 1024 are privileged.
Control panel equivalent
- WHM: Security Center > SSH Password Authorization Tweak > Disable Password Auth.
- Plesk: edit sshd_config through the SSH Terminal extension, same directives.
Step 5: Close every port you are not using
First, see what is actually listening:
sudo ss -tulpn
Anything bound to 0.0.0.0 or [::] is reachable from the internet. Databases, caches and admin tools should be bound to 127.0.0.1 instead.
UFW (Ubuntu / Debian)
sudo apt install ufw -y
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp # or your custom SSH port
sudo ufw allow 80,443/tcp
sudo ufw enable
sudo ufw status verbose
firewalld (AlmaLinux / Rocky)
sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
Which ports to keep open
| Port | Service | Recommendation |
|---|---|---|
| 22 | SSH / SFTP | Open, ideally restricted to your IP |
| 80 / 443 | HTTP / HTTPS | Open (80 only to redirect to 443) |
| 21 | FTP | Close it. Use SFTP or FTPS instead |
| 3306 / 5432 | MySQL / PostgreSQL | Close. Bind to localhost or use an SSH tunnel |
| 6379 / 11211 | Redis / Memcached | Close. These are classic ransomware entry points |
| 25 / 465 / 587 | SMTP | Only if you run a mail server. Never an open relay |
| 2375 / 2376 | Docker API | Never expose. Instant root compromise |
| 2082-2087 / 8443 | cPanel / Plesk | Restrict to your office or VPN IP range |
Docker warning: published Docker ports write their own iptables rules and can bypass UFW. Bind containers explicitly, for example -p 127.0.0.1:3306:3306.
Control panel equivalent
- WHM: ConfigServer Security & Firewall (CSF) > Firewall Configuration > edit TCP_IN / TCP_OUT.
- Plesk: Tools & Settings > Firewall > Manage Rules.
- Cloud dashboards: use the provider network firewall (Security Groups) as a second layer in front of the OS firewall.
Step 6: Install fail2ban to stop brute force attacks
fail2ban reads your logs, spots repeated failures and bans the offending IP at the firewall level.
sudo apt install fail2ban -y # or: sudo dnf install fail2ban -y
sudo nano /etc/fail2ban/jail.local
Paste a minimal configuration:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 4
backend = systemd
ignoreip = 127.0.0.1/8 YOUR.HOME.IP.ADDRESS
[sshd]
enabled = true
[nginx-http-auth]
enabled = true
[nginx-botsearch]
enabled = true
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
Always add your own IP to ignoreip, otherwise you will eventually lock yourself out. Keep a console or rescue access option from your provider handy.
Control panel equivalent
- Plesk: Tools & Settings > IP Address Banning (Fail2Ban), then enable the ssh, plesk-panel, roundcube and wordpress jails.
- WHM: cPHulk Brute Force Protection, plus CSF’s LFD (Login Failure Daemon).

Step 7: Schedule automatic security updates
Servers rarely get hacked on day one. They get hacked in month seven, when nobody has run apt upgrade since launch.
Ubuntu / Debian
sudo apt install unattended-upgrades apt-listchanges -y
sudo dpkg-reconfigure --priority=low unattended-upgrades
Then edit /etc/apt/apt.conf.d/50unattended-upgrades and enable:
Unattended-Upgrade::Mail "[email protected]";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";
AlmaLinux / Rocky
sudo dnf install dnf-automatic -y
sudo nano /etc/dnf/automatic.conf # set upgrade_type = security, apply_updates = yes
sudo systemctl enable --now dnf-automatic.timer
For zero-downtime kernel patching on production servers, consider a live patching service. Otherwise schedule a monthly reboot window.
Control panel equivalent
WHM: Server Configuration > Update Preferences > set cPanel and RPM updates to Automatic. Plesk: Tools & Settings > Update and Upgrade Settings > enable automatic updates. Also enable auto-updates for WordPress core, plugins and themes, since outdated CMS plugins cause more hacked sites than server misconfiguration.
Step 8: Fix file ownership and permissions
Permissions are where most self-hosted setups quietly fail. The web server user (www-data, nginx or apache) should be able to read your files, but not own or rewrite all of them.
Safe defaults
| Target | Permission | Notes |
|---|---|---|
| Directories | 755 | Never 777, under any circumstance |
| Files | 644 | Readable, not executable |
| wp-config.php / .env | 640 or 600 | Contains DB credentials |
| ~/.ssh | 700 | SSH refuses looser modes |
| ~/.ssh/authorized_keys | 600 | Owned by the user, not root |
| Upload folders | 755 + no PHP execution | Block script execution in the vhost |
cd /var/www/example.com
sudo chown -R deploy:www-data .
sudo find . -type d -exec chmod 755 {} \;
sudo find . -type f -exec chmod 644 {} \;
sudo chmod 640 wp-config.php
# Find dangerous world-writable files
sudo find /var/www -type f -perm -o+w -ls
Block PHP execution in upload directories (Nginx)
location ~* /(uploads|files|media)/.*\.(php|phar|phtml)$ {
deny all;
}
Apache equivalent (.htaccess inside the uploads folder)
<FilesMatch "\.(php|phar|phtml)$">
Require all denied
</FilesMatch>
Control panel equivalent
cPanel File Manager and Plesk File Manager both let you set permissions recursively. On shared hosting, also make sure account isolation is active (CageFS, mod_ruid2 or equivalent) so a neighbour’s hacked site cannot read your files.
Step 9: Install a TLS certificate and force HTTPS
Free certificates have no downside in 2026. Certbot handles issuing and renewal automatically.
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-run
Then add HSTS once you are sure everything works over HTTPS:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
Disable TLS 1.0 and 1.1, keep TLS 1.2 and 1.3 only.
Control panel equivalent
cPanel: Security > SSL/TLS Status > Run AutoSSL, then Domains > Force HTTPS Redirect. Plesk: SSL/TLS Certificates > Install a free Let’s Encrypt certificate, then tick “Redirect from http to https”.

Step 10: Lock down the database
sudo mysql_secure_installation
Answer yes to removing anonymous users, disallowing remote root login, and dropping the test database. Then confirm MySQL only listens locally in /etc/mysql/mysql.conf.d/mysqld.cnf:
bind-address = 127.0.0.1
Create one database user per application, with privileges limited to its own database:
CREATE DATABASE app_prod CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'a-long-random-passphrase';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, DROP ON app_prod.* TO 'app_user'@'localhost';
FLUSH PRIVILEGES;
Need remote access with a GUI client? Use an SSH tunnel rather than opening port 3306:
ssh -L 3307:127.0.0.1:3306 deploy@YOUR_SERVER_IP
Step 11: Set up off-server backups and test a restore
A backup stored on the same server is not a backup. If the machine is encrypted or deleted, both copies disappear at once. Follow the 3-2-1 rule: three copies, two media types, one off-site. For the wider picture, see Cloud Server Security Guide for Small Businesses.
- Use restic, borg or rclone to push to object storage (S3-compatible, Backblaze B2, Wasabi).
- Enable versioning and immutability / object lock on the bucket so ransomware cannot overwrite history.
- Keep daily backups for 7 days, weekly for 4 weeks, monthly for 6 months.
- Test a real restore onto a throwaway server at least once a quarter. Untested backups fail exactly when you need them.
sudo apt install restic -y
export RESTIC_REPOSITORY="s3:s3.example.com/my-backup-bucket"
restic init
restic backup /var/www /etc /home --exclude="*.log"
restic snapshots
Control panel equivalent
cPanel: Files > Backup, or JetBackup with a remote destination. Plesk: Tools & Settings > Backup Manager > Remote Storage (FTP, S3, Google Drive). Also keep your provider snapshots enabled, but do not rely on them alone.
Step 12: Add logging, alerts and a web application firewall
Once the basics are in place, the goal becomes knowing quickly when something changes.
- WAF: ModSecurity with the OWASP Core Rule Set, or a cloud WAF in front of your origin. It blocks SQL injection and common exploit patterns before they reach PHP.
- Malware scanning:
maldet(Linux Malware Detect) plus ClamAV, scheduled nightly on web roots. - File integrity: AIDE or Tripwire to alert when system binaries or theme files change unexpectedly.
- Log review:
journalctl -u ssh --since "24 hours ago",lastbfor failed logins, and a weekly look at/var/log/nginx/access.log. - Uptime and certificate expiry alerts by email or chat, so you notice an outage before your customers do.
- DDoS protection at the network edge, usually provided by your host or a CDN.
# Quick audit of your hardening work
sudo apt install lynis -y
sudo lynis audit system
Lynis gives you a hardening index and a prioritised list of remaining suggestions. Aim for 70+ on a web server, and re-run it after each major change.

Bonus: 5 mistakes that undo everything above
- Reusing the same password for the panel, FTP, MySQL and email. Use a password manager and unique credentials for each service.
- Leaving phpMyAdmin or Adminer publicly reachable. Restrict it by IP or tunnel to it.
- Running abandoned plugins and themes. Delete, do not just deactivate.
- Granting SSH to a freelancer and never revoking it. Remove the key line from
authorized_keyswhen the project ends. - Ignoring outbound traffic. A compromised server sending spam gets your IP blacklisted, and that reputation damage is slow to repair.
Your first 48 hours, hour by hour
| When | What to do |
|---|---|
| Hour 0 to 1 | Patch, create sudo user, SSH keys, disable root and password login |
| Hour 1 to 2 | Firewall rules, close unused ports, install fail2ban, enable auto-updates |
| Hour 2 to 4 | Deploy the site, set permissions, issue TLS, harden the database |
| Day 2 | Backups to remote storage, restore test, WAF, malware scan, Lynis audit |
| Every month | Review logs and users, confirm backups ran, re-run the audit |
Frequently asked questions
How can I secure my web server quickly?
Apply the four highest-impact changes first: install all pending updates, switch SSH to key-only authentication, disable root login, and enable a default-deny firewall that only allows ports 22, 80 and 443. Those four steps block the overwhelming majority of opportunistic automated attacks and take under 30 minutes.
Is self-hosting a website legal?
Yes. Hosting a website on your own VPS or on hardware you control is legal in virtually every country. What matters is compliance with the rules that apply to the content and the data: privacy regulations such as GDPR, consumer disclosure requirements, copyright, and your ISP’s terms of service, which often forbid running servers on a residential connection. Business-critical sites are usually better placed in a datacentre for uptime and bandwidth reasons.
Which is safer, www or HTTPS?
They are not alternatives. “www” is simply a subdomain prefix and has no effect on security. HTTPS is the encrypted protocol that protects the traffic between visitor and server. You should always use HTTPS, and you can use it with or without the www prefix. Pick one canonical version and 301 redirect the other for SEO consistency.
What is the most secure web hosting service?
No provider is secure by itself. The safest setup combines a host that offers isolated accounts, free TLS, automated off-site backups, a WAF and DDoS mitigation with a server you have actually hardened. A well-configured entry-level VPS is more secure than a premium managed plan where nobody ever changed the default settings. Judge providers on patch cadence, backup retention, 2FA support and incident response time rather than marketing claims.
Do I still need fail2ban if I use SSH keys only?
Yes, though for different reasons. With password authentication disabled, SSH brute force cannot succeed, but fail2ban still trims log noise and protects other surfaces such as web login forms, mail authentication and control panel logins.
Is 777 ever acceptable for a folder?
No. If an application only works at 777, the ownership is wrong. Set the folder owner to the user the web server runs as, or add that user to the owning group, and keep directories at 755 and files at 644.
How often should I check my server security?
Automate what you can (updates, backups, fail2ban, alerts), then perform a manual review monthly: user accounts and SSH keys, open ports, backup restore status, certificate expiry and log anomalies. Run a full audit with a tool like Lynis every quarter.
Need this done for you?
Server hardening is a one-time job with permanent benefits, but it is easy to lock yourself out or miss a detail that matters. Our team audits, hardens and monitors web hosting servers for businesses that would rather focus on their website than on sshd_config. Get in touch and we will review your current setup against this exact checklist.
