This guide walks you through the essential steps to harden your server for Drupal 11.x. You'll learn what to do, why it matters, and how to implement each security measure effectively.
Why Server Security Hardening Matters More Than You Think for Drupal CMS
Server vulnerabilities create multiple attack vectors that bypass your Drupal security entirely. Attackers can:
- Access your database directly
- Inject malicious code into your files
- Steal sensitive data before it reaches Drupal
- Use your server to attack other systems
The 2024 Drupal security report showed that 73% of compromised Drupal sites had server-level vulnerabilities, not application-level ones. This means your first line of defense isn't your Drupal configuration—it's your server setup.
Step 1: Keep Your Drupal Software Stack Current and Updated
Start with your foundation: outdated software is the easiest target for attackers.
Update Drupal Core and Security Modules
Check for updates weekly, not monthly. Set up automatic notifications:
# Enable update notifications in Drupal drush pm:enable update drush updatedb
Always remove unused modules and themes. Each installed component increases your attack surface, even if it's disabled.
Maintain Your Server Software Stack
Use Long-Term Support (LTS) versions when possible:
- Ubuntu 24.04 LTS or RHEL 9 for your OS
- PHP 8.2 or 8.3 (avoid older versions)
- MariaDB 10.11 or PostgreSQL 15
- Nginx 1.24 or Apache 2.4.57
Set up automatic security updates for your OS:
# Ubuntu/Debian sudo apt install unattended-upgrades sudo dpkg-reconfigure unattended-upgrades # CentOS/RHEL sudo yum install yum-cron sudo systemctl enable yum-cron
Step 2: Implement HTTPS SSL Certificate and Security Headers
Install SSL/TLS Certificates for Drupal
Use Let's Encrypt for free certificates or purchase commercial ones for business sites:
# Install Certbot sudo apt install certbot python3-certbot-nginx # Get certificate sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Configure Security Headers for Drupal Sites
Add these headers to your Nginx configuration:
server { listen 443 ssl http2; server_name yourdomain.com; # Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; }
Test your headers using Mozilla Observatory to ensure they're working correctly.
Step 3: Secure User Access and Authentication for Drupal Admin
Enable Two-Factor Authentication for Drupal
Install the TFA module for admin accounts:
composer require drupal/tfa drush en tfa
Configure it to require 2FA for all users with administrative roles.
Set Strong Password Policies in Drupal
Use the Password Policy module:
composer require drupal/password_policy drush en password_policy
Configure minimum requirements:
- 12 characters minimum
- Mix of uppercase, lowercase, numbers, and symbols
- No common passwords or dictionary words
- No reuse of last 5 passwords
Limit User Permissions in Drupal CMS
Review user roles monthly. Remove permissions that users don't actively need. Create specific roles for different tasks instead of giving everyone administrative access.
Step 4: Harden File and Directory Permissions for Drupal Sites
Set Correct File Permissions for Drupal
Your Drupal files need specific permissions to function securely:
# Set file permissions find /var/www/html -type f -exec chmod 644 {} \; # Set directory permissions find /var/www/html -type d -exec chmod 755 {} \; # Secure sensitive files chmod 400 /var/www/html/sites/default/settings.php chmod 400 /var/www/html/sites/default/services.yml
Prevent PHP Execution in Upload Directories
Add this to your .htaccess file in the files directory:
Require all denied
For Nginx, add this to your server block:
location ~* ^/sites/.*/files/.*\.php$ { deny all; }
Move Private Files Outside Web Root
Configure private file storage outside your web directory:
// In settings.php $settings['file_private_path'] = '/var/drupal/private';
Create the directory and set permissions:
sudo mkdir -p /var/drupal/private sudo chown www-data:www-data /var/drupal/private sudo chmod 755 /var/drupal/private
Step 5: Configure Web Server Security for Drupal Hosting
Disable Directory Listings
For Apache, add this to your configuration:
Options -Indexes
For Nginx:
autoindex off;
Restrict Access to Sensitive Drupal Files
Block access to Drupal's administrative files:
# Apache .htaccess Require ip 127.0.0.1 Require ip ::1
# Nginx location ~ ^/(cron|install|update|authorize)\.php$ { allow 127.0.0.1; allow ::1; deny all; try_files $uri =404; fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; fastcgi_index index.php; include fastcgi_params; }
Step 6: Harden PHP Configuration for Drupal Performance
Disable Dangerous PHP Functions
Edit your php.ini file:
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
Set Security Directives for PHP
expose_php = Off allow_url_fopen = Off allow_url_include = Off enable_dl = Off display_errors = Off log_errors = On max_execution_time = 30 max_input_time = 30 memory_limit = 256M upload_max_filesize = 10M post_max_size = 10M
Configure Open Basedir for Drupal
Restrict PHP to your Drupal directory:
open_basedir = /var/www/html:/tmp:/var/tmp
Step 7: Secure Your Drupal Database Configuration
Create a Restricted Database User for Drupal
Don't use the root user for Drupal. Create a specific user with minimal permissions:
CREATE USER 'drupal_user'@'localhost' IDENTIFIED BY 'strong_password_here'; CREATE DATABASE drupal_db; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, LOCK TABLES ON drupal_db.* TO 'drupal_user'@'localhost'; FLUSH PRIVILEGES;
Bind Database to Localhost
In your MySQL configuration (/etc/mysql/mysql.conf.d/mysqld.cnf):
bind-address = 127.0.0.1
Change the Default Table Prefix
In your Drupal settings.php:
$databases['default']['default']['prefix'] = 'dp_';
This makes SQL injection attacks harder by obscuring standard table names.
Step 8: Configure Network Security for Drupal Server
Set Up a Firewall for Drupal Hosting
Configure UFW (Uncomplicated Firewall) on Ubuntu:
sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow 22/tcp # SSH sudo ufw allow 80/tcp # HTTP sudo ufw allow 443/tcp # HTTPS sudo ufw enable
Harden SSH Access for Drupal Server
Edit /etc/ssh/sshd_config:
Port 2222 # Change from default 22 PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes MaxAuthTries 3 ClientAliveInterval 300 ClientAliveCountMax 2
Install and configure Fail2ban to block brute force attempts:
sudo apt install fail2ban sudo systemctl enable fail2ban sudo systemctl start fail2ban
Step 9: Implement Monitoring and Logging for Drupal Security
Set Up Centralized Logging
Configure rsyslog to collect all security events:
# In /etc/rsyslog.d/50-drupal.conf local0.* /var/log/drupal.log
Install Security Monitoring Tools
Set up file integrity monitoring with AIDE:
sudo apt install aide sudo aide --init sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
Create a daily cron job to check for changes:
# Add to crontab 0 2 * * * /usr/bin/aide --check
Monitor Failed Login Attempts in Drupal
Use the Login Security module:
composer require drupal/login_security drush en login_security
Configure it to block IP addresses after 5 failed attempts.
Step 10: Automate Backups and Recovery for Drupal Sites
Set Up Automated Drupal Backups
Create a backup script that runs daily:
#!/bin/bash # backup-drupal.sh DATE=$(date %Y%m%d_%H%M%S) BACKUP_DIR="/var/backups/drupal" DRUPAL_ROOT="/var/www/html" DB_NAME="drupal_db" DB_USER="drupal_user" DB_PASS="your_password" mkdir -p $BACKUP_DIR # Backup database mysqldump -u$DB_USER -p$DB_PASS $DB_NAME | gzip > $BACKUP_DIR/db_$DATE.sql.gz # Backup files tar -czf $BACKUP_DIR/files_$DATE.tar.gz -C $DRUPAL_ROOT sites/default/files # Keep only last 7 days of backups find $BACKUP_DIR -name "*.gz" -mtime 7 -delete
Test Your Drupal Backups
Create a staging environment and regularly test backup restoration. A backup you can't restore is worthless.
Common Drupal Security Mistakes to Avoid
Running services as root: Always use dedicated users with minimal permissions.
Ignoring log files: Set up log monitoring and review them regularly.
Using default ports: Change SSH and other service ports from defaults.
Skipping security updates: Apply security patches within 24-48 hours of release.
Weak file permissions: Regularly audit and correct file permissions.
Your Next Steps for Drupal Server Hardening
- Audit your current setup using the Security Review module
- Implement these changes gradually to avoid breaking your site
- Test each change in a staging environment first
- Document your security configuration for future reference
- Set up monitoring alerts for security events
- Schedule regular security reviews monthly
Server security isn't a one-time setup—it's an ongoing process. Start with the most critical items (HTTPS, updates, and backups) and work through the rest systematically. Your Drupal site will be much more secure with a properly hardened server foundation.
Remember: the best security strategy combines multiple layers of protection. No single measure will protect you from all threats, but implementing these steps together creates a robust defense system that makes your Drupal site a much harder target for attackers.