A comprehensive tutorial for setting up a production-ready WordPress site on AWS with OpenLiteSpeed web server, free SSL, Redis caching, and enterprise-level security.
Total Time: 60-90 minutes
Cost: £0/month (6 months AWS free tier)
Difficulty: Intermediate
By the end of this guide, you’ll have a fully optimized WordPress site with:
✅ AWS EC2 server (London region for UK/EU speed)
✅ OpenLiteSpeed web server (faster than Apache/Nginx)
✅ Free SSL certificate (Let’s Encrypt)
✅ Redis object caching (database optimization)
✅ A+ security rating (headers, firewall, fail2ban)
✅ Email functionality (Brevo SMTP)
✅ HTTP/2 and HTTP/3 support
✅ Automatic security updates
AWS Account – free tier eligible
Domain name – example.com, pointed to AWS after setup
SSH client – Terminal on Mac/Linux, PuTTY on Windows
Basic command line knowledge
WordPress backup – files + database export if migrating existing site
Log into AWS Console and navigate to EC2. Click “Launch Instance”.
Configure instance:
Name: example.com
Application and OS Images (AMI):
Select “Ubuntu”
Choose “Ubuntu Server 24.04 LTS (HVM), SSD Volume Type”
Verify “Free tier eligible” badge
Instance type: t3.micro (1 vCPU, 1GB RAM – free tier)
Key pair (login):
Click “Create new key pair”
Name: wordpress-key
Type: RSA
Format: .pem (Mac/Linux) or .ppk (Windows)
Download and save – you need this to connect!
Network settings:
Click “Edit”
Security group name: wordpress-sg
Add these rules:
SSH (22) – Source: My IP
HTTP (80) – Source: Anywhere (0.0.0.0/0)
HTTPS (443) – Source: Anywhere (0.0.0.0/0)
Custom TCP (7080) – Source: My IP (for WebAdmin)
Configure storage: 30 GiB (free tier includes up to 30GB)
Click “Launch Instance”
Get your Public IP:
Go to Instances
Select your instance
Copy “Public IPv4 address” (e.g., 18.133.138.227)
Mac/Linux:
#Navigate to where your key was downloaded
cd ~/Downloads
#Set correct permissions
chmod 400 wordpress-key.pem
#Connect (replace YOUR-IP with your instance's public IP)
ssh -i wordpress-key.pem ubuntu@YOUR-IP
Windows (PowerShell):
ssh -i C:\Users\YourName\Downloads\wordpress-key.pem ubuntu@YOUR-IP
First connection: Type yes when asked to continue.
You should now see: ubuntu@ip-xxx-xxx-xxx-xxx:~$ ✅
sudo apt update && sudo apt upgrade -y
wget -O - https://repo.litespeed.sh | sudo bash sudo apt update
sudo apt install openlitespeed lsphp83 lsphp83-common lsphp83-mysql lsphp83-curl lsphp83-opcache lsphp83-intl lsphp83-imap lsphp83-pspell lsphp83-sqlite3 lsphp83-tidy lsphp83-redis -y
sudo apt install mariadb-server -y
sudo systemctl start lsws sudo systemctl enable lsws sudo systemctl start mariadb sudo systemctl enable mariadb
sudo ln -sf /usr/local/lsws/lsphp83/bin/php /usr/bin/php
In browser, visit: http://YOUR-IP:8088
You should see the OpenLiteSpeed welcome page! ✅
sudo /usr/local/lsws/admin/misc/admpass.sh
Enter username: admin
Enter password: create strong password
Save these credentials!
sudo mkdir -p /usr/local/lsws/example.com/html sudo mkdir -p /usr/local/lsws/example.com/logs sudo mkdir -p /usr/local/lsws/conf/vhosts/example.com
docRoot $VH_ROOT/html
vhDomain example.com, www.example.com
vhAliases example.com, www.example.com
enableGzip 1
enableIpGeo 1
errorlog $VH_ROOT/logs/error.log {
useServer 0
logLevel ERROR
rollingSize 10M
}
accesslog $VH_ROOT/logs/access.log {
useServer 0
logFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\""
logHeaders 5
rollingSize 10M
keepDays 30
compressArchive 1
}
index {
useServer 0
indexFiles index.php, index.html
}
scripthandler {
add lsapi:lsphp83 php
}
extprocessor lsphp83 {
type lsapi
address uds://tmp/lshttpd/lsphp.sock
maxConns 35
env PHP_LSAPI_CHILDREN=35
env LSAPI_AVOID_FORK=200M
initTimeout 60
retryTimeout 0
persistConn 1
respBuffer 0
autoStart 2
path $SERVER_ROOT/lsphp83/bin/lsphp
backlog 100
instances 1
priority 0
memSoftLimit 2047M
memHardLimit 2047M
procSoftLimit 1400
procHardLimit 1500
}
rewrite {
enable 1
autoLoadHtaccess 1
rules <<<END_rules
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
END_rules
}
context / {
location $DOC_ROOT/
allowBrowse 1
rewrite {
enable 1
inherit 1
}
addDefaultCharset off
extraHeaders <<<END_extraHeaders
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=()
END_extraHeaders
phpIniOverride {
php_admin_value engine On
}
}
context /xmlrpc.php {
allowBrowse 0
accessControl {
deny ALL
}
}
Save: Ctrl + X → Y → Enter
sudo nano /usr/local/lsws/conf/httpd_config.conf
Find the listener Default section (around line 185) and update:
listener Default {
address *:80
secure 0
map example.com example.com, www.example.com
}
Scroll to the bottom and add:
virtualhost example.com {
vhRoot /usr/local/lsws/example.com
configFile /usr/local/lsws/conf/vhosts/example.com/vhconf.conf
allowSymbolLink 1
enableScript 1
restrained 1
setUIDMode 2
}
Save: Ctrl + X → Y → Enter
sudo /usr/local/lsws/bin/lswsctrl restart
sudo mysql_secure_installation
Follow prompts:
Enter current password: Press Enter (empty) Switch to unix_socket: n Change root password: Y → Enter strong password Remove anonymous users: Y Disallow root remote login: Y Remove test database: Y Reload privileges: Y
sudo mysql -u root -p
Enter your root password, then run:
CREATE DATABASE wordpress; CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'StrongPassword123!'; GRANT ALL PRIVILEGES ON wordpress.* TO 'wpuser'@'localhost'; FLUSH PRIVILEGES; EXIT;
Save these credentials – you’ll need them!
cd /usr/local/lsws/example.com/html sudo wget https://wordpress.org/latest.tar.gz sudo tar -xzvf latest.tar.gz sudo mv wordpress/* . sudo rm -rf wordpress latest.tar.gz
sudo cp wp-config-sample.php wp-config.php sudo nano wp-config.php
Update database settings:
define( 'DB_NAME', 'wordpress' ); define( 'DB_USER', 'wpuser' ); define( 'DB_PASSWORD', 'StrongPassword123!' ); // Your password from Step 4.2 define( 'DB_HOST', 'localhost' );
Generate security keys at: https://api.wordpress.org/secret-key/1.1/salt/
Copy the generated keys and replace the dummy keys in wp-config.php
Add these security settings before /* That’s all, stop editing! */:
define('DISALLOW_FILE_EDIT', true);
define('FORCE_SSL_ADMIN', true);
define('WP_AUTO_UPDATE_CORE', 'minor');
define('WP_POST_REVISIONS', 3);
define('AUTOSAVE_INTERVAL', 300);
Save: Ctrl + X → Y → Enter
sudo chown -R nobody:nogroup /usr/local/lsws/example.com/html
sudo chmod -R 755 /usr/local/lsws/example.com/html
sudo find /usr/local/lsws/example.com/html -type f -exec chmod 644 {} ;
sudo chmod 600 /usr/local/lsws/example.com/html/wp-config.php
Visit: http://YOUR-IP
Complete the WordPress installation wizard with site title, username, password, and email.
WordPress is now installed! ✅
At your domain registrar/DNS provider, add A records:
Type: A Name: @ Value: YOUR-EC2-IP TTL: 3600 Type: A Name: www Value: YOUR-EC2-IP TTL: 3600
Wait 5-10 minutes for DNS propagation
cd ~ curl https://get.acme.sh | sh source ~/.bashrc
#Temporarily change ownership for validation
sudo chown -R ubuntu:ubuntu /usr/local/lsws/example.com/html
#Request certificate
~/.acme.sh/acme.sh --issue -d example.com -d www.example.com -w /usr/local/lsws/example.com/html
#Change ownership back
sudo chown -R nobody:nogroup /usr/local/lsws/example.com/html
#Create SSL directory
sudo mkdir -p /usr/local/lsws/conf/cert/example.com
#Copy certificates
sudo cp ~/.acme.sh/example.com_ecc/fullchain.cer /usr/local/lsws/conf/cert/example.com/fullchain.pem sudo cp ~/.acme.sh/example.com_ecc/example.com.key /usr/local/lsws/conf/cert/example.com/key.pem sudo cp ~/.acme.sh/example.com_ecc/example.com.cer /usr/local/lsws/conf/cert/example.com/cert.pem
#Set permissions
sudo chmod 600 /usr/local/lsws/conf/cert/example.com/key.pem sudo chmod 644 /usr/local/lsws/conf/cert/example.com/*.pem
sudo nano /usr/local/lsws/conf/httpd_config.conf
Add after the listener Default section:
listener SSL {
address *:443
secure 1
keyFile /usr/local/lsws/conf/cert/example.com/key.pem
certFile /usr/local/lsws/conf/cert/example.com/fullchain.pem
certChain 1
sslProtocol 24
map example.com example.com, www.example.com
}
Save: Ctrl + X → Y → Enter
mysql -u wpuser -p wordpress
UPDATE wp_options SET option_value = 'https://example.com' WHERE option_name = 'siteurl'; UPDATE wp_options SET option_value = 'https://example.com' WHERE option_name = 'home'; EXIT;
sudo nano /usr/local/lsws/conf/vhosts/example.com/vhconf.conf
Update the rewrite section:
rewrite {
enable 1
autoLoadHtaccess 1
rules <<<END_rules
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
RewriteBase /
RewriteRule ^index.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
END_rules
}
Save and restart:
sudo /usr/local/lsws/bin/lswsctrl restart
Test: https://example.com – Should show green padlock! 🔒
sudo apt install ufw -y
#Default policies
sudo ufw default deny incoming sudo ufw default allow outgoing
#Allow necessary ports
sudo ufw allow 22/tcp # SSH sudo ufw allow 80/tcp # HTTP sudo ufw allow 443/tcp # HTTPS sudo ufw allow 7080/tcp # WebAdmin
#Enable firewall
sudo ufw enable
#Verify
sudo ufw status
sudo apt install fail2ban -y
Create configuration:
sudo nano /etc/fail2ban/jail.local
Add:
[DEFAULT] bantime = 3600 findtime = 600 maxretry = 5 destemail = your-email@example.com action = %(action_mwl)s [sshd] enabled = true port = 22 logpath = /var/log/auth.log [wordpress-auth] enabled = true filter = wordpress-auth logpath = /usr/local/lsws/example.com/logs/access.log maxretry = 3 port = http,https
Create WordPress filter:
sudo nano /etc/fail2ban/filter.d/wordpress-auth.conf
[Definition]
failregex = ^<HOST> .* "POST /wp-login.php
^<HOST> .* "POST /xmlrpc.php
ignoreregex =
Start Fail2Ban:
sudo systemctl restart fail2ban sudo systemctl enable fail2ban sudo fail2ban-client status
sudo nano /usr/local/lsws/lsphp83/etc/php/8.3/litespeed/php.ini
Update these settings:
expose_php = Off display_errors = Off log_errors = On allow_url_fopen = Off allow_url_include = Off upload_max_filesize = 64M post_max_size = 64M max_execution_time = 300 memory_limit = 256M disable_functions = passthru,shell_exec,system,proc_open,popen
Save and restart:
sudo /usr/local/lsws/bin/lswsctrl restart
sudo apt install unattended-upgrades -y sudo dpkg-reconfigure -plow unattended-upgrades
Select Yes
By default, OpenLiteSpeed WebAdmin is accessible on port 7080 from any IP. For maximum security, restrict it to localhost-only and access via SSH tunnel.
sudo nano /usr/local/lsws/admin/conf/admin_config.conf
Find the listener section and change:
listener adminListener {
address *:7080To:
listener adminListener {
address 127.0.0.1:7080Save: Ctrl + X → Y → Enter
sudo /usr/local/lsws/bin/lswsctrl restart
### Step 6.5.3: Verify WebAdmin is Localhost-Only
sudo ss -tlnp | grep 7080
Should show: 127.0.0.1:7080 (not *:7080 or 0.0.0.0:7080)
**Mac/Linux:**
Create an alias in ~/.zshrc or ~/.bash_profile:
alias nsadmin="ssh -i /path/to/wordpress-key.pem -L 7080:localhost:7080 ubuntu@YOUR-IP"
Reload configuration:
source ~/.zshrc
**Windows (PowerShell):**
Create a function in your PowerShell profile:
function nsadmin { ssh -i C:\path\to\wordpress-key.pem -L 7080:localhost:7080 ubuntu@YOUR-IP }**1. Open terminal and connect:**
nsadmin
**2. Keep terminal window open**
**3. Open browser and visit:**
http://localhost:7080
**4. Login with your admin credentials**
**Important:**
– Use http:// not https:// for localhost
– Keep the SSH terminal running while using WebAdmin
– When you close SSH, WebAdmin access stops (by design)
### Security Benefits:
✅ WebAdmin completely invisible from internet
✅ No exposure even if firewall misconfigured
✅ Requires SSH authentication before WebAdmin access
✅ No additional attack surface on port 7080
sudo apt install redis-server -y sudo systemctl start redis-server sudo systemctl enable redis-server
sudo nano /etc/redis/redis.conf
Find and update:
maxmemory 256mb maxmemory-policy allkeys-lru
Restart:
sudo systemctl restart redis-server
Login to WordPress admin: https://example.com/wp-admin
Go to Plugins → Add New
Search “LiteSpeed Cache”
Install → Activate
LiteSpeed Cache → Cache → Object
Object Cache: Turn ON
Method: Redis
Host: localhost
Port: 6379
Save Changes
Test Connection – Should show “Connected” ✅
Cache tab:
Cache Mobile: ON
TTL: 604800 (1 week)
Page Optimization:
CSS Minify: ON
CSS Combine: ON
JavaScript Minify: ON
HTML Minify: ON
Image Optimization:
Auto Pull Images: ON
WebP Replacement: ON
Sign up at https://www.brevo.com/ (free – 300 emails/day)
Verify your email
Complete account setup
In Brevo Dashboard:
Settings → SMTP & API
Click “API Keys” tab
Generate a new API key
Name: “WordPress”
Copy the key (starts with xkeysib-…)
In Brevo:
Settings → Senders & IP
Domains → Add a domain
Enter: example.com
Add DNS records they provide to your domain’s DNS
Typical DNS records:
Type: TXT Name: @ Value: brevo-code=xxxxx Type: CNAME Name: mail._domainkey Value: (Brevo provides)
Update SPF record:
v=spf1 include:spf.brevo.com ~all
In WordPress:
Plugins → Add New
Search “WP Mail SMTP”
Install → Activate
WP Mail SMTP → Settings:
From Email: noreply@example.com (or your email)
From Name: Your Site Name
Mailer: Brevo
API Key: Paste your Brevo API key
Save Settings
Test: WP Mail SMTP → Tools → Send test email
#Install Postfix
sudo apt install postfix mailutils -y
Select Internet Site and set system mail name to example.com
Configure for Brevo:
sudo nano /etc/postfix/main.cf
Add at bottom:
relayhost = [smtp-relay.brevo.com]:587 smtp_sasl_auth_enable = yes smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd smtp_sasl_security_options = noanonymous smtp_tls_security_level = encrypt smtp_use_tls = yes
Get Brevo SMTP credentials from: Brevo Dashboard → SMTP & API → SMTP tab → Create SMTP key
Create credentials file:
sudo nano /etc/postfix/sasl_passwd
Add (replace with your credentials):
[smtp-relay.brevo.com]:587 your-login@smtp-brevo.com:your-smtp-key
Apply:
sudo chmod 600 /etc/postfix/sasl_passwd sudo postmap /etc/postfix/sasl_passwd sudo systemctl restart postfix
Test server email:
echo "Test from server" | mail -s "Test" your-email@example.com
curl -I https://example.com
Should show:
Strict-Transport-Security
X-Frame-Options
X-Content-Type-Options
X-XSS-Protection
Online test: https://securityheaders.com/?q=example.com
Visit: https://www.ssllabs.com/ssltest/analyze.html?d=example.com
Target: A or A+ rating
Visit: https://pagespeed.web.dev/
Enter: example.com
Target: 90+ score
Visit: https://www.mail-tester.com/
Send test email to address shown
Target: 8-10/10 score
sudo nano /usr/local/bin/backup-wordpress.sh
Add:
#!/bin/bash BACKUP_DIR="/home/ubuntu/backups" DATE=$(date +%Y%m%d_%H%M%S) DB_USER="wpuser" DB_PASS="YourPassword" DB_NAME="wordpress" mkdir -p $BACKUP_DIR mysqldump -u $DB_USER -p$DB_PASS $DB_NAME | gzip > $BACKUP_DIR/db_$DATE.sql.gz tar -czf $BACKUP_DIR/files_$DATE.tar.gz /usr/local/lsws/example.com/html/ find $BACKUP_DIR -type f -mtime +7 -delete echo "Backup completed: $DATE"
Make executable:
sudo chmod +x /usr/local/bin/backup-wordpress.sh
Schedule daily backups:
crontab -e
Add:
0 2 * * * /usr/local/bin/backup-wordpress.sh >> /home/ubuntu/backup.log 2>&1
#Check disk space
df -h
#Check memory usage
free -h
#Check OpenLiteSpeed status
sudo systemctl status lsws
#Check Redis status
sudo systemctl status redis-server
#Check fail2ban status
sudo fail2ban-client status
Recommended Swap Size:
For 1GB RAM → 2GB swap (2x RAM is standard)
# Check if swap exists sudo swapon --show free -h # Create 2GB swap file sudo fallocate -l 2G /swapfile # Set correct permissions sudo chmod 600 /swapfile # Make it a swap file sudo mkswap /swapfile # Enable swap sudo swapon /swapfile # Verify free -h # Make permanent (survives reboots) echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab # Optimize swappiness (lower = use RAM more, swap less) sudo sysctl vm.swappiness=10 echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
Restart OpenLiteSpeed:
sudo /usr/local/lsws/bin/lswsctrl restart
Check OpenLiteSpeed logs:
sudo tail -f /usr/local/lsws/example.com/logs/error.log
Restart Redis:
sudo systemctl restart redis-server
Check Redis connection:
redis-cli ping # Should return PONG
View mail logs:
sudo tail -f /var/log/mail.log
Renew SSL certificate:
~/.acme.sh/acme.sh --renew -d example.com
WebAdmin access: https://YOUR-IP:7080
Solution: #Check virtual host mapping
sudo nano /usr/local/lsws/conf/httpd_config.conf
#Verify "map example.com" line exists in listeners
sudo /usr/local/lsws/bin/lswsctrl restart
Solution: #Verify certificate files exist
sudo ls -la /usr/local/lsws/conf/cert/example.com/
#Check permissions
sudo chmod 600 /usr/local/lsws/conf/cert/example.com/key.pem sudo chmod 644 /usr/local/lsws/conf/cert/example.com/fullchain.pem
Solution:
#Check Postfix status
sudo systemctl status postfix
#Check mail logs
sudo tail -50 /var/log/mail.log
#Test SMTP connection
telnet smtp-relay.brevo.com 587
Solution: #Restart Redis
sudo systemctl restart redis-server
#Check Redis status
redis-cli ping
#Restart OpenLiteSpeed
sudo /usr/local/lsws/bin/lswsctrl restart
EC2 t3.micro: £0/month
30GB EBS storage: £0/month
Data transfer: £0/month (15GB free)
SSL certificate: £0/month (Let’s Encrypt)
Email (Brevo): £0/month (300 emails/day)
Total: £0/month
EC2 t3.micro: ~£8.50/month
30GB EBS storage: ~£3/month
Data transfer: Variable
Email (Brevo): £0/month (still free)
Estimated Total: ~£12-15/month
What to expect:
Page load time: 0.5-1.5 seconds
Time to first byte (TTFB): 100-300ms
PageSpeed score: 90-100
Security headers grade: A+
SSL rating: A or A+
Concurrent users supported: 50-100 (t2.micro)
CDN: Add Cloudflare for global content delivery
Monitoring: Setup uptime monitoring (UptimeRobot, Pingdom)
Backup offsite: Sync backups to S3 or external storage
Scaling: Upgrade to t2.small or t3.small for high traffic
Database: Move to RDS for better reliability (larger sites)
Install WordFence: Advanced firewall and malware scanning
Two-factor auth: Enable 2FA for WordPress admin
SSH key only: Disable password authentication
Restrict WebAdmin: Firewall WebAdmin port to specific IPs
Regular audits: Monthly security reviews
You now have a production-ready WordPress site running on AWS EC2 with:
✅ Performance: OpenLiteSpeed + Redis = lightning fast
✅ Security: A+ rating with enterprise-level protection
✅ Reliability: Free tier eligible for 12 months
✅ Email: Fully functional SMTP (300/day free)
✅ SSL: Free HTTPS with auto-renewal
✅ Modern: HTTP/2, HTTP/3, latest PHP 8.3
Total setup cost: £0/month (first 6 month ec2 Amazon)
Performance vs shared hosting: 3-5x faster
Security vs typical WordPress: Significantly hardened
This setup rivals premium managed WordPress hosting costing £50-100/month!
Official Documentation:
OpenLiteSpeed: https://docs.litespeedtech.com/
AWS EC2: https://docs.aws.amazon.com/ec2/
WordPress: https://wordpress.org/documentation/
LiteSpeed Cache: https://docs.litespeedtech.com/lscache/
Community:
OpenLiteSpeed Forum: https://forum.openlitespeed.org/
WordPress Support: https://wordpress.org/support/
AWS Community: https://repost.aws/
Last Updated: April 30, 2026
Tested On: Ubuntu 24.04 LTS, PHP 8.3, OpenLiteSpeed 1.7+
Author: Nuno / nuno-sarmento.com
Questions or issues? Leave a comment below or contact me at hello@nuno-sarmento.com
Did this guide help you? Share it with others who might benefit!