WPFleet includes comprehensive security features to protect your WordPress sites.
WPFleet provides multiple layers of security:
- Automatic HTTPS with Let's Encrypt certificates
- Security headers configured by default
- Container isolation for process separation
- File access restrictions to protect sensitive files
- Network security with Docker networking
- XML-RPC blocking to prevent brute force attacks
- Regular security updates via Docker images
What it does:
- Automatically obtains SSL certificates from Let's Encrypt
- Renews certificates before expiration
- Redirects HTTP to HTTPS
- Enforces secure connections
Configuration:
Configured automatically in Caddy for each site:
example.com {
# Automatic HTTPS enabled by default
tls {
protocols tls1.2 tls1.3
}
}Monitor SSL certificates:
./scripts/ssl-monitor.shWPFleet configures these security headers for all sites:
Content Security Policy (CSP):
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';
HTTP Strict Transport Security (HSTS):
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Frame-Options:
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options:
X-Content-Type-Options: nosniff
X-XSS-Protection:
X-XSS-Protection: 1; mode=block
Referrer Policy:
Referrer-Policy: strict-origin-when-cross-origin
Permissions Policy:
Permissions-Policy: geolocation=(), microphone=(), camera=()
Caddy blocks access to sensitive files:
wp-config.php- WordPress configuration.git/- Git repositories.env- Environment files.htaccess- Apache configuration*.sql- Database dumps*.log- Log files
Configured in Caddy:
# Block sensitive files
@forbidden {
path /wp-config.php /.git/* /.env .htaccess *.sql *.log
}
respond @forbidden 403XML-RPC is blocked by default to prevent:
- Brute force attacks
- DDoS amplification
- Pingback spam
Configured in Caddy:
# Block XML-RPC
@xmlrpc path /xmlrpc.php
respond @xmlrpc 403To enable XML-RPC if needed:
Edit config/caddy/sites/example.com.caddy and remove the XML-RPC block.
Each service runs in isolated containers:
- FrankenPHP: Runs as
www-data(unprivileged) - MariaDB: Isolated database with limited access
- Valkey: Password-protected cache service
- Cron: Separate container for scheduled tasks
Benefits:
- Process isolation prevents cross-contamination
- Limited attack surface per container
- Easy to restart or update individual services
Docker network isolation:
networks:
wpfleet:
driver: bridge
internal: falseSecurity features:
- Services communicate via internal Docker network
- MariaDB and Valkey not exposed to host
- Only FrankenPHP exposes ports 80 and 443
Strong passwords required:
MYSQL_ROOT_PASSWORD=generate_strong_password_here
MYSQL_PASSWORD=generate_strong_password_hereGenerate secure passwords:
openssl rand -base64 32Database access is restricted:
- Root user only accessible from within Docker network
- Application user (
wpfleet) has limited permissions - No external port exposure by default
From host machine (requires Docker exec):
docker exec -it wpfleet_mariadb mysql -uroot -p${MYSQL_ROOT_PASSWORD}For external access (not recommended for production):
# Create SSH tunnel instead
ssh -L 3306:localhost:3306 user@your-serverValkey requires authentication:
REDIS_PASSWORD=generate_strong_password_hereConfigured in Valkey:
requirepass your_password_here
These commands are disabled for security:
FLUSHALL- Delete all keysFLUSHDB- Delete database keysCONFIG- Change configurationKEYS- List all keys (performance risk)
Configured in docker/valkey/valkey.conf:
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""
rename-command KEYS ""
Valkey only accessible via Docker network, not exposed to host.
Proper permissions are critical:
# Set correct ownership
docker exec wpfleet_frankenphp chown -R www-data:www-data /var/www/html/example.com
# Set directory permissions
find data/wordpress/example.com -type d -exec chmod 755 {} \;
# Set file permissions
find data/wordpress/example.com -type f -exec chmod 644 {} \;WPFleet configures secure wp-config.php:
// Disable file editing in WordPress admin
define( 'DISALLOW_FILE_EDIT', true );
// Limit post revisions
define( 'WP_POST_REVISIONS', 5 );
// Force SSL for admin
define( 'FORCE_SSL_ADMIN', true );
// Security keys (automatically generated)
define( 'AUTH_KEY', 'unique-key-here' );
// ... more keysConfigured in Caddy to prevent directory browsing:
file_server {
hide .git
disable_canonical_headers
}Use strong passwords for:
- MySQL root and application users
- Valkey authentication
- WordPress admin accounts
- SSH access
Generate passwords:
openssl rand -base64 32Regularly update:
# Update Docker images
docker-compose pull
docker-compose up -d
# Update WordPress core
./scripts/wp-cli.sh example.com core update
# Update plugins
./scripts/wp-cli.sh example.com plugin update --all
# Update themes
./scripts/wp-cli.sh example.com theme update --allInstall WordPress security plugins:
# Wordfence Security
./scripts/wp-cli.sh example.com plugin install wordfence --activate
# iThemes Security
./scripts/wp-cli.sh example.com plugin install better-wp-security --activate
# Sucuri Security
./scripts/wp-cli.sh example.com plugin install sucuri-scanner --activateConfigure login attempt limiting in WordPress security plugins or use Cloudflare.
Enable 2FA for WordPress admin:
./scripts/wp-cli.sh example.com plugin install two-factor --activateMaintain regular backups:
BACKUP_ENABLED=true
BACKUP_SCHEDULE="0 2 * * *"See Backups for details.
Regularly check logs for suspicious activity:
docker logs wpfleet_frankenphp | grep -i "error\|warning"Use a firewall (UFW on Ubuntu):
# Allow SSH
sudo ufw allow 22/tcp
# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Enable firewall
sudo ufw enableOnly run necessary services:
# List running containers
docker ps
# Stop unused containers
docker stop container_nameUse a CDN with DDoS protection:
- Cloudflare (free plan available)
- AWS CloudFront
- Fastly
- KeyCDN
Cloudflare setup:
- Add your domain to Cloudflare
- Update nameservers
- Enable SSL/TLS (Full mode)
- Enable DDoS protection
- Configure firewall rules
- Enable rate limiting
Benefits:
- DDoS protection
- Rate limiting
- Bot protection
- WAF (Web Application Firewall)
- Global CDN
Install Fail2Ban on host to block brute force:
sudo apt-get install fail2banConfigure for WordPress:
# /etc/fail2ban/jail.local
[wordpress]
enabled = true
filter = wordpress
logpath = /path/to/wpfleet/data/logs/*.log
maxretry = 5
bantime = 3600Add ModSecurity for additional protection:
# In docker-compose.yml
services:
waf:
image: owasp/modsecurity-crs:nginx
ports:
- "80:80"
- "443:443"
environment:
- BACKEND=http://frankenphp:8080Use this checklist for new installations:
- Generate strong passwords for all services
- Configure
.envwith secure values - Enable HTTPS for all sites
- Configure security headers
- Block XML-RPC if not needed
- Set proper file permissions
- Install WordPress security plugin
- Enable two-factor authentication
- Configure regular backups
- Set up monitoring and alerts
- Enable firewall (UFW)
- Configure fail2ban
- Use CDN/DDoS protection
- Disable directory listing
- Remove default plugins and themes
- Disable file editing in WordPress
- Limit login attempts
- Keep software updated
- Review user accounts and permissions
- Monitor logs regularly
Add security checks to cron:
#!/bin/bash
# security-check.sh
# Check for weak passwords in users
WEAK=$(docker exec wpfleet_mariadb mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "SELECT user FROM mysql.user WHERE password = '' OR password IS NULL")
if [ -n "$WEAK" ]; then
./scripts/notify.sh error "Security Issue" "Found users with weak passwords"
fi
# Check for outdated plugins
OUTDATED=$(./scripts/wp-cli.sh example.com plugin list --update=available --format=count)
if [ $OUTDATED -gt 0 ]; then
./scripts/notify.sh warning "Updates Available" "$OUTDATED plugins need updates"
fiAdd to cron:
CUSTOM_CRON_JOBS="0 0 * * * cd /wpfleet && ./security-check.sh"Regularly audit security:
# Check running containers
docker ps
# Check open ports
sudo netstat -tlnp
# Check file permissions
find data/wordpress -type f -perm 777
# Check for malware (install ClamAV)
clamscan -r data/wordpress/
# Check SSL certificates
./scripts/ssl-monitor.sh
# Review user accounts
docker exec wpfleet_mariadb mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e "SELECT user, host FROM mysql.user"If you suspect a security breach:
-
Isolate affected sites:
docker stop wpfleet_frankenphp
-
Review logs:
docker logs wpfleet_frankenphp > breach-logs.txt -
Check for malware:
clamscan -r data/wordpress/example.com
-
Restore from backup:
./scripts/site-manager.sh remove example.com ./scripts/site-manager.sh add example.com --import-from
-
Update all passwords:
- MySQL passwords
- Valkey password
- WordPress admin passwords
- SSH keys
-
Update all software:
docker-compose pull ./scripts/wp-cli.sh example.com core update ./scripts/wp-cli.sh example.com plugin update --all
-
Review access logs for suspicious activity
-
Notify users if data was compromised
- OWASP Top 10
- WordPress Security
- Docker Security
- Let's Encrypt
- WPScan - WordPress vulnerability scanner