Skip to content
Back to Blog
Security11 min read

Server Security Hardening 2026: 9 Steps [Solved]

A practical checklist that blocks most automated attacks: SSH hardening, firewall rules, fail2ban, kernel tweaks, and monitoring that actually works.

Written by Abdul AbrorTechnical Hosting Support Engineer
Server Security Hardening 2026: 9 Steps [Solved]
On this page

Most server breaches start with automated scanners probing for lazy defaults. They try root SSH logins, scan for open services, and brute-force anything that responds. A solid hardening checklist stops these attacks cold before they escalate.

I've seen hosting accounts compromised because SSH ran on port 22 with password auth enabled, or because a firewall rule allowed inbound traffic from anywhere. The fixes are straightforward once you know what to lock down. This guide walks through nine hardening steps that work on both Linux and Windows servers, focusing on the changes that have the biggest impact.

1. Disable root login and enforce key-based SSH

Password authentication over SSH is a gift to brute-force bots. Disable it entirely and use SSH keys instead.

On Linux, edit /etc/ssh/sshd_config:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no

Restart SSH:

systemctl restart sshd

Before you close the current session, open a second terminal and test key login to make sure you don't lock yourself out. If you must allow a specific user with a strong password, use AllowUsers to whitelist only that account. But keys are better.

For Windows Server with OpenSSH, the same sshd_config directives apply. The config file lives in C:\ProgramData\ssh\sshd_config. Restart the service with Restart-Service sshd.

2. Change the default SSH port

Port 22 gets hammered by scanners 24/7. Moving SSH to a non-standard port (anything above 1024 and below 65535) cuts bot noise by more than half.

Edit /etc/ssh/sshd_config:

Port 2223

Restart SSH and update your firewall rules to allow the new port. If you use SELinux, tell it about the new port:

semanage port -a -t ssh_port_t -p tcp 2223

This won't stop a determined attacker, but it eliminates the spray-and-pray bots.

3. Configure a host firewall with deny-by-default

Every server should run a local firewall—iptables, nftables, or firewalld on Linux; Windows Firewall on Windows Server. Default policy: drop everything inbound except the services you explicitly need.

On a typical web + SSH server using ufw (Ubuntu/Debian):

ufw default deny incoming
ufw default allow outgoing
ufw allow 2223/tcp comment 'SSH'
ufw allow 80/tcp comment 'HTTP'
ufw allow 443/tcp comment 'HTTPS'
ufw enable

For firewalld (RHEL/CentOS/AlmaLinux):

firewall-cmd --set-default-zone=drop
firewall-cmd --zone=public --add-port=2223/tcp --permanent
firewall-cmd --zone=public --add-service=http --permanent
firewall-cmd --zone=public --add-service=https --permanent
firewall-cmd --reload

On Windows Server, use New-NetFirewallRule in PowerShell or the GUI to allow RDP (if needed), HTTP/HTTPS, and block everything else inbound.

Check your rules with ufw status verbose or firewall-cmd --list-all. If a service stops working, you forgot to open its port.

4. Install and tune fail2ban or equivalent

Fail2ban watches logs for repeated failed login attempts and temporarily bans the offending IP. It's cheap insurance against brute-force.

Install on Debian/Ubuntu:

apt update && apt install fail2ban

On RHEL-based systems:

dnf install epel-release
dnf install fail2ban

Create /etc/fail2ban/jail.local to override defaults:

[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3

[sshd]
enabled = true
port = 2223

Restart fail2ban:

systemctl restart fail2ban
systemctl enable fail2ban

Check active bans with fail2ban-client status sshd. In support tickets I handled, the most common mistake was forgetting to update the port number in the jail config after changing SSH's port.

Windows admins can use tools like EvlWatcher or IPBan to achieve similar results by monitoring Event Viewer logs.

5. Keep the kernel and packages current

Unpatched servers are low-hanging fruit. Automate updates where possible, or at least check weekly.

On Debian/Ubuntu, enable unattended-upgrades for security patches:

apt install unattended-upgrades
dpkg-reconfigure --priority=low unattended-upgrades

Edit /etc/apt/apt.conf.d/50unattended-upgrades to control what gets updated automatically. Security updates only is a safe starting point.

On RHEL-based systems, use dnf-automatic:

dnf install dnf-automatic
systemctl enable --now dnf-automatic.timer

For Windows Server, enable automatic updates in Windows Update settings or use WSUS if you manage multiple machines. Reboot schedules matter—pick a maintenance window and stick to it.

Check pending updates manually:

# Debian/Ubuntu
apt list --upgradable

# RHEL/CentOS
dnf check-update

6. Disable unused services and close unnecessary ports

Every running service is a potential attack surface. If you're not using it, turn it off.

List active services on Linux:

systemctl list-units --type=service --state=running

Disable anything you don't recognize or need:

systemctl disable --now <service-name>

Common candidates: cups (printing), avahi-daemon (mDNS), rpcbind (NFS), postfix (if you use an external mail relay).

Check listening ports:

ss -tuln

If something is listening on 0.0.0.0 or :: and you don't know why, investigate. Bind services to 127.0.0.1 or a specific internal IP if they don't need public access.

On Windows, use Get-Service in PowerShell and netstat -an to audit services and listeners.

7. Harden kernel parameters with sysctl

A few sysctl tweaks make your Linux server more resistant to network attacks and resource exhaustion.

Create /etc/sysctl.d/99-hardening.conf:

# Ignore ICMP ping requests
net.ipv4.icmp_echo_ignore_all = 1

# Disable IP forwarding unless you're routing
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0

# Enable SYN cookies (protects against SYN flood)
net.ipv4.tcp_syncookies = 1

# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# Log suspicious packets
net.ipv4.conf.all.log_martians = 1

Apply the settings:

sysctl -p /etc/sysctl.d/99-hardening.conf

These won't stop a DDoS, but they reduce the noise from scans and probes.

8. Set up centralized logging and alerts

You can't respond to an attack if you don't see it happening. Send logs to a remote server or a log aggregation service so an attacker can't erase the evidence.

On Linux, configure rsyslog to forward to a central server:

# /etc/rsyslog.d/50-remote.conf
*.* @@logserver.example.com:514

Restart rsyslog:

systemctl restart rsyslog

For smaller setups, rotate logs aggressively and back them up daily. Check /var/log/auth.log (Debian/Ubuntu) or /var/log/secure (RHEL) for SSH login attempts.

Set up email or Slack alerts for critical events—repeated failed SSH logins, disk usage above 90%, or services crashing. Tools like logwatch or custom scripts with cron work fine.

Windows admins should forward Event Viewer logs to a SIEM or at least export Security logs weekly.

9. Run a rootkit scanner and file integrity monitor

Assume breach. Install tools that detect tampering even if an attacker gets in.

For rootkit detection, use rkhunter or chkrootkit:

# Debian/Ubuntu
apt install rkhunter
rkhunter --update
rkhunter --check --sk

Schedule weekly scans via cron:

0 3 * * 0 /usr/bin/rkhunter --check --cronjob --report-warnings-only

For file integrity, AIDE (Advanced Intrusion Detection Environment) watches critical system files and alerts you to changes:

apt install aide
aideinit
cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db

Run checks daily:

0 4 * * * /usr/bin/aide --check

If AIDE reports changes to /bin, /sbin, or /usr/bin that you didn't make, investigate immediately.

On Windows, enable File Integrity Monitoring in Windows Defender or use a third-party tool like Tripwire.

What happens if you skip these steps?

Servers left with default configs get compromised fast. I've restored accounts where attackers installed cryptominers, sent spam, or used the server as a pivot to scan internal networks. The fix took hours; the breach could have been prevented in thirty minutes.

Automated attacks don't need skill—they just need you to leave a door open. Close the obvious ones and you're no longer the easy target.

How often should I review these settings?

Quarterly at minimum. Check after any major software update or whenever you add a new service. Security configs drift over time as admins add exceptions and forget to remove them.

Does changing the SSH port really help?

Yes, but not against targeted attacks. It stops the bots that scan every IP on port 22. Your auth.log will go from thousands of daily attempts to maybe a handful. That alone makes troubleshooting easier.

Should I disable ping completely?

Up to you. Blocking ICMP hides your server from casual scans, but it also breaks some legitimate network diagnostics. I usually leave it enabled unless the server is under constant probe traffic.

Can I automate all of this with a script?

Most of it, yes. Configuration management tools like Ansible, Puppet, or Chef let you apply the same hardening baseline to dozens of servers. Write the playbook once, run it everywhere.

What if I lock myself out during hardening?

Always keep a second SSH session open while you're editing configs. If you're working on a remote VPS without console access, test each change before closing your active connection. Most hosting panels offer emergency console access—know where yours is before you need it.

The first thing to harden

Start with SSH. Disable root login, enforce keys, move the port, and set up fail2ban. That single change blocks the majority of automated attacks you'll see in logs. Then work through the firewall, updates, and monitoring in whatever order fits your workflow. A hardened server isn't invincible, but it's no longer the easiest mark on the block.