Skip to content
Back to Blog
Security11 min read

Server Security Hardening Checklist [2026 Updated]

Close attack vectors on Linux and Windows servers before production with this step-by-step hardening checklist covering SSH, firewall rules, user accounts, and patch management.

Written by Abdul AbrorTechnical Hosting Support Engineer
Server Security Hardening Checklist [2026 Updated]
On this page

A fresh server is a liability until you harden it. Default SSH ports, root login enabled, no firewall rules—common patterns that turn up in breach post-mortems. This checklist covers the baseline tasks I walk through on every Linux and Windows server before it sees production traffic.

You can finish most of these in an hour. Some require planning (patch schedules, backup retention). All of them reduce your attack surface measurably.

Initial Access and Account Hygiene

First thing: stop using root or Administrator for day-to-day work.

Disable root SSH login (Linux)

Create a non-root user with sudo privileges, then lock out direct root access over SSH. Open /etc/ssh/sshd_config and set:

PermitRootLogin no

Restart SSH:

systemctl restart sshd

Test the new user account in a second terminal before you close your root session. I've seen admins lock themselves out by skipping this step.

Enforce key-based authentication

Passwords are guessable. SSH keys are not. Generate a key pair on your local machine:

ssh-keygen -t ed25519 -C "[email protected]"

Copy the public key to the server:

ssh-copy-id user@server_ip

Then disable password authentication in /etc/ssh/sshd_config:

PasswordAuthentication no
ChallengeResponseAuthentication no

Restart SSH and confirm you can still connect with your key.

Rename or disable Administrator (Windows)

The built-in Administrator account is a known target. Rename it via Local Security Policy or disable it entirely if you have another admin account. Create a new user with administrative privileges and a non-obvious name.

Open Computer Management → Local Users and Groups → Users. Right-click Administrator, choose Rename, and pick something that doesn't advertise privilege.

Network Surface Reduction

Open ports are open doors.

Change the default SSH port

Port 22 gets hammered by bots. Moving SSH to a non-standard port (say, 2200 or 49222) cuts the noise. Edit /etc/ssh/sshd_config:

Port 2200

Update your firewall rules to allow the new port before restarting SSH, or you'll lock yourself out. For UFW:

ufw allow 2200/tcp
ufw delete allow 22/tcp
systemctl restart sshd

This is security through obscurity, yes. It still works.

Configure a firewall and default-deny

Linux servers should run a firewall with a default-deny policy. UFW is the simplest starting point on Ubuntu/Debian:

ufw default deny incoming
ufw default allow outgoing
ufw allow 2200/tcp  # SSH
ufw allow 80/tcp    # HTTP
ufw allow 443/tcp   # HTTPS
ufw enable

For CentOS/RHEL, use firewalld:

firewall-cmd --set-default-zone=public
firewall-cmd --add-service=ssh --permanent
firewall-cmd --add-service=http --permanent
firewall-cmd --add-service=https --permanent
firewall-cmd --reload

On Windows Server, enable Windows Firewall with default inbound block. Open Windows Defender Firewall with Advanced Security and configure rules for RDP (if needed), HTTP, and HTTPS only.

Close unused services

List listening ports and kill anything you don't recognize:

ss -tulnp

Common culprits: Sendmail running when you use an external SMTP relay, old MySQL instances, Telnet (disable immediately). Stop and disable them:

systemctl stop sendmail
systemctl disable sendmail

On Windows, open Services (services.msc) and set startup type to Disabled for unused services like Remote Registry, Print Spooler (if not printing), and older SMB versions.

Patch Management and Automatic Updates

Unpatched software is the easiest entry point.

Enable automatic security updates (Linux)

On Debian/Ubuntu, install unattended-upgrades:

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

Edit /etc/apt/apt.conf.d/50unattended-upgrades to auto-reboot if required:

Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";

For RHEL/CentOS, dnf-automatic does the same job:

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

Configure Windows Update

Open Settings → Update & Security → Windows Update → Advanced options. Set active hours to avoid surprise reboots during business time. Enable automatic installation of updates.

For Windows Server, consider WSUS if you manage multiple machines. Manual patching doesn't scale.

Keep your control panel and web stack updated

If you run cPanel, WHM, Plesk, or DirectAdmin, check for updates weekly. Same for PHP, MySQL, Apache, or Nginx. A forgotten PHP 7.2 instance with known CVEs is a liability.

cPanel example:

/scripts/upcp

For manual stacks, subscribe to security mailing lists for your software and test updates on staging first.

User Privilege and Password Policy

Enforce strong password requirements

Install and configure libpam-pwquality on Linux:

apt install libpam-pwquality

Edit /etc/security/pwquality.conf:

minlen = 14
minclass = 3
maxrepeat = 2

This enforces 14-character passwords with at least three character classes and blocks excessive repetition.

On Windows, open Local Security Policy → Account Policies → Password Policy. Set minimum length to 14, enable complexity requirements, and set maximum password age to 90 days.

Limit sudo/admin access

Don't hand out sudo to every user. Create a dedicated admin group and grant it sudo access via /etc/sudoers:

%admingroup ALL=(ALL:ALL) ALL

Use visudo to edit safely. For Windows, avoid adding users to the Administrators group unless they genuinely need system-wide access. Use the Remote Desktop Users group for remote access without admin rights.

Audit user accounts regularly

List all users on Linux:

cut -d: -f1 /etc/passwd

Delete or lock unused accounts:

usermod -L olduser
userdel -r olduser

On Windows, open Computer Management → Local Users and Groups → Users and disable stale accounts.

Logging, Monitoring, and Intrusion Detection

You can't respond to what you don't see.

Centralize and retain logs

Configure syslog forwarding to a remote log server or a service like Papertrail. If the server is compromised, local logs can be wiped. Remote logs survive.

Edit /etc/rsyslog.conf and add:

*.* @@logserver.example.com:514

Restart rsyslog:

systemctl restart rsyslog

On Windows, forward Event Logs using Windows Event Forwarding (WEF) to a collector server.

Install Fail2Ban (Linux)

Fail2Ban blocks IPs after repeated failed login attempts. Install it:

apt install fail2ban

Copy the default config:

cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Edit jail.local to enable SSH protection:

[sshd]
enabled = true
port = 2200
maxretry = 3
bantime = 3600

Restart Fail2Ban:

systemctl restart fail2ban

Enable audit logging

On Linux, install auditd to track file access, command execution, and privilege escalation:

apt install auditd
systemctl enable auditd

Add rules in /etc/audit/rules.d/audit.rules to monitor sensitive files:

-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes

Reload rules:

auditctl -R /etc/audit/rules.d/audit.rules

On Windows, enable Object Access auditing via Group Policy or Local Security Policy to track file and registry changes.

File System and Permission Hardening

Set correct ownership and permissions

Web root directories should not be world-writable. For Apache/Nginx on Linux:

chown -R www-data:www-data /var/www/html
find /var/www/html -type d -exec chmod 755 {} \;
find /var/www/html -type f -exec chmod 644 {} \;

Never set chmod 777 on production files. If an application demands it, the application is broken.

Disable unused filesystems

Edit /etc/modprobe.d/disable-filesystems.conf and add:

install cramfs /bin/true
install freevxfs /bin/true
install jffs2 /bin/true
install hfs /bin/true
install hfsplus /bin/true
install udf /bin/true

These are rarely needed and widen the kernel attack surface.

Enable disk encryption

If the server stores sensitive data, encrypt the disk. On Linux, use LUKS at install time. For existing volumes, migrating to encrypted partitions requires downtime and backups.

On Windows Server, enable BitLocker via Server Manager → File and Storage Services → Volumes. Store recovery keys securely off-server.

Application and Service Hardening

Harden Apache

Hide version information. Edit your Apache config (often /etc/apache2/conf-available/security.conf):

ServerTokens Prod
ServerSignature Off

Disable directory listing:

<Directory /var/www/html>
    Options -Indexes
</Directory>

Restart Apache:

systemctl restart apache2

Harden Nginx

Hide the version in responses. Add to nginx.conf:

server_tokens off;

Set security headers in your server block:

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;

Reload Nginx:

nginx -s reload

Harden MySQL/MariaDB

Run the security script:

mysql_secure_installation

Remove anonymous users, disable remote root login, and drop the test database. Bind MySQL to localhost unless you need remote access:

[mysqld]
bind-address = 127.0.0.1

Restart MySQL:

systemctl restart mysql

Backup and Disaster Recovery

Hardening doesn't prevent every breach. Backups do.

Automate daily backups

Use rsync, Restic, or Duplicity to back up to remote storage. Schedule via cron:

0 2 * * * /usr/local/bin/backup.sh

Test restores quarterly. Untested backups are wishes.

Store backups off-server

Local backups are useless if the server is ransomwared. Use S3, Backblaze B2, or a secondary VPS in a different datacenter.

Implement snapshot policies

If your host offers filesystem or VM snapshots, schedule daily snapshots with seven-day retention. Snapshots aren't backups, but they buy you rollback time.

Final Sweep

So what if you've checked every box here but still feel exposed?

Run a vulnerability scanner

Use OpenVAS, Nessus, or Lynis (for Linux) to find misconfigurations you missed:

lynis audit system

Review the report and address high-priority findings.

Enable two-factor authentication

For SSH, install Google Authenticator PAM module:

apt install libpam-google-authenticator

Run google-authenticator as your user and follow the prompts. Edit /etc/pam.d/sshd to add:

auth required pam_google_authenticator.so

In /etc/ssh/sshd_config, set:

ChallengeResponseAuthentication yes

Restart SSH. You'll now need both your key and a TOTP code.

For Windows RDP, Azure MFA or Duo can integrate with Network Policy Server.

Document your configuration

Write down what you changed, where, and why. Future-you will thank present-you when you need to audit or replicate the setup.

What You Can Skip (and What You Can't)

You can defer disk encryption if you don't handle payment data or personal information. You can skip intrusion detection on a low-value staging box.

You cannot skip firewalls, SSH hardening, or patch management. Those three are the baseline. Everything else adds depth, but without the baseline you're already breached—you just don't know it yet.

Run through this list before your next deployment. An hour now beats a weekend incident response later.

FAQ

How often should I re-harden a server?

Audit quarterly. Review users, open ports, and installed software. Patch weekly or enable automatic updates.

Can I automate this checklist?

Yes. Use Ansible, Puppet, or Chef to codify these steps. Configuration management ensures consistency across multiple servers and makes re-hardening trivial.

What's the biggest mistake you see?

Leaving SSH on port 22 with password authentication enabled. It's low-hanging fruit for attackers and trivial to fix.

Do I need all of this for a staging server?

Staging should mirror production security. If staging is compromised, attackers can learn your stack and pivot to production.

Is changing the SSH port really necessary?

No, but it cuts bot traffic by 90% and makes log review easier. Think of it as reducing noise, not as a security control.