Security Hardening
A hardened OpenLiteSpeed server applies multiple overlapping layers of protection. This cheatsheet covers every layer — work through them in order.
Layer 1: Firewall — Lock Ports First
# === UFW (Ubuntu/Debian) ===
# Allow only necessary ports
sudo ufw allow 80/tcp comment "HTTP"
sudo ufw allow 443/tcp comment "HTTPS"
# WebAdmin — ONLY from your IP
sudo ufw allow from YOUR.IP.HERE to any port 7080 comment "OLS WebAdmin"
# Block all other access to 7080
sudo ufw deny 7080/tcp
sudo ufw enable
sudo ufw status numbered
# === firewalld (AlmaLinux/Rocky) ===
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="YOUR.IP" port protocol="tcp" port="7080" accept'
sudo firewall-cmd --permanent --add-rich-rule='rule port protocol="tcp" port="7080" reject'
sudo firewall-cmd --reload
Layer 2: SSL/TLS Configuration
In httpd_config.conf — Listener SSL Block
listener HTTPS {
address *:443
secure 1
keyFile /etc/ssl/private/example.key
certFile /etc/ssl/certs/example.crt
certChain 1
# TLS 1.2 + 1.3 only (value = sum: 8=TLS1.1, 16=TLS1.2, 32=TLS1.3)
# 16+32=48 for TLS 1.2+1.3
sslProtocol 48
# Enable ECDHE for forward secrecy
enableECDHE 1
enableDHE 1
# HTTP/2 via ALPN
alpn h2,http/1.1
# Prevent TLS renegotiation attacks
renegProtection 1
# Strong cipher suite (TLS 1.2)
ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
map example *
}
Verify TLS from CLI
# Test TLS handshake
openssl s_client -connect example.com:443 -servername example.com
# Check certificate expiry
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -enddate
# Check what TLS version is negotiated
openssl s_client -connect example.com:443 -tls1_3 2>&1 | grep "Protocol"
# Scan TLS config (install testssl.sh)
# bash testssl.sh example.com
Layer 3: HTTP Security Headers
Add these via a context block or .htaccess:
.htaccess security headers
<IfModule mod_headers.c>
# Prevent clickjacking
Header always set X-Frame-Options "SAMEORIGIN"
# Prevent MIME sniffing
Header always set X-Content-Type-Options "nosniff"
# XSS protection (legacy browsers)
Header always set X-XSS-Protection "1; mode=block"
# Referrer policy
Header always set Referrer-Policy "strict-origin-when-cross-origin"
# Permissions policy
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
# HSTS — tell browsers to always use HTTPS (1 year)
# Only add AFTER you are sure HTTPS is working correctly
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Content Security Policy (adjust for your app)
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;"
</IfModule>
Verify Headers
# Check headers from CLI
curl -I https://example.com/ | grep -iE "X-Frame|X-Content|Strict-Transport|X-XSS|Content-Security"
# Or with verbose output
curl -sv https://example.com/ 2>&1 | grep "<"
Layer 4: File Access Controls
.htaccess — block sensitive files
# Block access to hidden files/dirs (except .well-known)
<FilesMatch "^\.(?!well-known)">
Order allow,deny
Deny from all
</FilesMatch>
# Block config files
<FilesMatch "\.(env|conf|ini|log|bak|sql|sh)$">
Order allow,deny
Deny from all
</FilesMatch>
# Block WordPress-specific targets
<Files xmlrpc.php>
Order deny,allow
Deny from all
</Files>
<Files wp-config.php>
Order deny,allow
Deny from all
</Files>
# Block PHP execution in uploads directory
<Directory /var/www/example.com/public/wp-content/uploads>
<FilesMatch "\.php$">
Order deny,allow
Deny from all
</FilesMatch>
</Directory>
Layer 5: IP Restrictions
Via vhconf.conf
# Restrict entire vhost to specific IPs
accessControl {
deny all
allow 192.168.1.0/24, 203.0.113.10
}
# Or restrict a specific context
context /admin {
accessControl {
deny all
allow 203.0.113.10
}
}
Via .htaccess
# Allow only specific IPs (Apache-compatible)
Order deny,allow
Deny from all
Allow from 203.0.113.10
Allow from 192.168.1.0/24
Layer 6: Rate Limiting
Configure in httpd_config.conf at server level:
# Connection throttling per IP
connLimit {
maxConnections 100 # max simultaneous connections per IP
dynReqPerSec 25 # dynamic requests per second per IP
staticReqPerSec 50 # static requests per second per IP
outBandwidth 0 # 0 = unlimited
}
# Bandwidth throttling
throttle {
staticReqPerSec 50
dynReqPerSec 25
outBandwidth 0
}
Layer 7: ModSecurity WAF
OpenLiteSpeed supports ModSecurity (modsec) rules.
# Install ModSecurity for OLS
sudo apt install mod-security-openlitespeed # check repo for exact package name
# Or use OWASP CRS rules
# Download and place in: /usr/local/lsws/conf/modsec/
# Enable in httpd_config.conf
module mod_security {
modsecurity on
modsecurity_rules_file /usr/local/lsws/conf/modsec/main.conf
}
Layer 8: WordPress-Specific Protections
wp-admin IP restriction in .htaccess
# Protect wp-admin
<Directory /var/www/example.com/public/wp-admin>
Order deny,allow
Deny from all
Allow from YOUR.IP.HERE
</Directory>
# Allow admin-ajax.php (required for WordPress AJAX)
<Files admin-ajax.php>
Order allow,deny
Allow from all
</Files>
# Block wp-login.php brute force at OLS level (context in vhconf.conf)
# Add this context:
context /wp-login.php {
accessControl {
allow YOUR.IP.HERE
deny all
}
}
Security Audit Checklist
# 1. Verify only ports 80 and 443 are public
sudo ss -tlnp | grep lshttpd
# 2. Check TLS protocol version
openssl s_client -connect example.com:443 2>&1 | grep "Protocol"
# 3. Verify HSTS header
curl -I https://example.com/ | grep Strict
# 4. Check for sensitive file exposure
curl -I https://example.com/.env
curl -I https://example.com/wp-config.php.bak
# Both should return 403 or 404
# 5. Verify WebAdmin is not reachable from internet
curl -k --connect-timeout 3 https://example.com:7080/ && echo "EXPOSED!" || echo "Protected"
# 6. Check PHP version not exposed
curl -I https://example.com/ | grep X-Powered-By
# Should be empty or removed
# 7. Verify SSL cert validity
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
Hide PHP Version in php.ini
; Remove X-Powered-By header
expose_php = Off
Hide Server Version in httpd_config.conf
# In server config
showVersionNumber 0