Skip to content

Ghost Security — Hardening, SSL, Firewalls and Protection

DodaTech Updated 2026-06-28 10 min read

In this tutorial, you'll learn how to secure Ghost CMS — hardening the Ghost installation with system-level security, configuring SSL certificates via Let's Encrypt, setting up UFW and fail2ban firewalls, protecting against common attacks, and securing the admin panel against unauthorized access.

What You'll Learn

  • Security architecture of a Ghost installation
  • SSL/TLS certificate setup with Let's Encrypt
  • UFW firewall rules for Ghost
  • Fail2ban configuration to block brute force attacks
  • Admin panel security (2FA, trusted IPs, Rate Limiting)
  • Securing Nginx reverse proxy
  • Database security best practices
  • File permissions and ownership
  • Regular security audits and updates
  • Backup security (encrypted backups)

Why It Matters

Ghost sites are frequent targets for automated attacks — brute force login attempts, SQL Injection probes, and vulnerability scanners. A compromised Ghost installation can leak member data, serve malware to visitors, or be used to send spam. Security is not optional for a production site. A single breach can damage your reputation, cost you members, and take days to clean up.

Real-World Use

A Ghost membership site storing 10,000 subscriber email addresses and payment data is targeted by a brute force attack on the admin panel at /ghost/. The attacker runs a dictionary attack with 1,000 attempts per minute. Fail2ban detects 5 failed login attempts from the same IP within 60 seconds and bans that IP for 1 hour. The attacker switches IPs but is slowed by rate limiting. The admin has 2FA enabled on their account, so even if the password is guessed, the account is not compromised.

Learning Path

flowchart LR
  A["Upgrading Ghost"] --> B["Security
You are here"]:::current B --> C["Monitoring & Logging"] C --> D["Production Deployment"] classDef current fill:#38bdf8,color:#0f172a,stroke-width:2px

Security Layers

A secure Ghost site has multiple layers:

  1. System: OS hardening, firewall, fail2ban
  2. Transport: SSL/TLS encryption
  3. Application: Ghost security features
  4. Database: Secure credentials, encrypted backups
  5. Admin: 2FA, rate limiting, IP whitelisting

Layer 1: System Hardening

File Permissions

Ghost files should have restricted permissions:

# Ghost directory ownership
sudo chown -R ghost_user:ghost_user /var/www/ghost

# Content directory should be writable
sudo chmod -R 755 /var/www/ghost/content/

# Configuration files should be readable only by Ghost user
sudo chmod 600 /var/www/ghost/config.production.json

# Core files should be read-only
sudo chmod -R 644 /var/www/ghost/current/core/

SSH Security

Edit /etc/ssh/sshd_config:

Port 2222  # Change from default 22
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2

Then restart SSH:

sudo systemctl restart sshd

Automatic Security Updates

# Install unattended-upgrades
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades

Layer 2: SSL/TLS

Using Certbot (Let's Encrypt)

# Install Certbot for Nginx
sudo apt install certbot python3-certbot-nginx -y

# Get and install certificate
sudo certbot --nginx -d yoursite.com -d www.yoursite.com

# Test auto-renewal
sudo certbot renew --dry-run

SSL Best Practices

# In /etc/nginx/sites-available/yoursite.com

server {
    listen 443 ssl http2;
    server_name yoursite.com;

    ssl_certificate /etc/letsencrypt/live/yoursite.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yoursite.com/privkey.pem;

    # Modern SSL config
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # HSTS (uncomment after testing)
    # add_header Strict-Transport-Security "max-age=63072000" always;
}

HTTP to HTTPS Redirect

server {
    listen 80;
    server_name yoursite.com www.yoursite.com;
    return 301 https://$server_name$request_uri;
}

Layer 3: UFW Firewall

# Enable UFW
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH on custom port
sudo ufw allow 2222/tcp

# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Allow Ghost admin only from specific IPs (optional)
sudo ufw allow from 203.0.113.0/24 to any port 2368

# Enable
sudo ufw enable
sudo ufw status verbose

Layer 4: Fail2ban

Install and Configure Fail2ban

sudo apt install fail2ban -y

Create /etc/fail2ban/jail.local:

[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5

[sshd]
enabled = true
port = 2222
maxretry = 3

[nginx-http-auth]
enabled = true

Ghost Login Protection

Fail2ban can protect the Ghost admin login by monitoring Nginx access logs:

Create /etc/fail2ban/filter.d/ghost-admin.conf:

[Definition]
failregex = POST /ghost/api/admin/session/
ignoreregex =

Add to jail.local:

[ghost-admin]
enabled = true
port = 443
filter = ghost-admin
logpath = /var/log/nginx/access.log
maxretry = 10
findtime = 300
bantime = 3600

Restart fail2ban:

sudo systemctl restart fail2ban
sudo fail2ban-client status

Layer 5: Admin Panel Security

Using a Custom Admin Path

Ghost does not support changing the admin path from /ghost/ by default, but you can add Nginx rules to restrict access:

location /ghost/ {
    # Allow only specific IPs
    allow 203.0.113.0/24;
    deny all;

    proxy_pass http://127.0.0.1:2368;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

Two-Factor Authentication (2FA)

Enable 2FA in Ghost settings:

  • Go to Settings → Staff → Your Profile → Enable Two-Factor Authentication
  • Scan the QR code with an authenticator app (Google Authenticator, Authy)
  • Save the recovery codes in a secure location

Strong Password Policy

Enforce strong passwords:

  • Minimum 12 characters
  • Mix of uppercase, lowercase, numbers, and symbols
  • No common words or patterns
  • Unique password per service (use a password manager)

Session Management

{
  "auth": {
    "session_expiry_ms": 86400000
  }
}

Layer 6: Nginx Security Headers

Add security headers in your Nginx configuration:

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 Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline' https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' https: data:; font-src 'self' https: data:; connect-src 'self' https:; media-src 'self' https:; object-src 'none'; frame-ancestors 'self';" always;

Layer 7: Database Security

Secure Database Credentials

{
  "database": {
    "client": "mysql",
    "connection": {
      "host": "127.0.0.1",
      "port": 3306,
      "user": "ghost_user",
      "password": "a-long-unique-password-here",
      "database": "ghost_production"
    }
  }
}
  • Never use default or weak passwords
  • Store config.production.json with 600 permissions
  • Use a separate database user per application
  • Revoke unused database users

Encrypting Backups

# Encrypt backup with GPG
gpg --symmetric --cipher-algo AES256 ghost_backup.sql.gz

# Decrypt when needed
gpg --decrypt ghost_backup.sql.gz.gpg > ghost_backup.sql.gz

Layer 8: Regular Security Audits

Automated Audit Checklist

Run this monthly:

#!/bin/bash
echo "=== Security Audit ==="

# Check SSL expiry
echo "SSL Expiry:"
openssl s_client -connect yoursite.com:443 -servername yoursite.com </dev/null 2>/dev/null \
  | openssl x509 -noout -dates

# Check for unauthorized users
echo "System Users:"
awk -F: '$3 >= 1000 && $3 < 65534 {print $1}' /etc/passwd

# Check file permissions
echo "Config File Permissions:"
ls -la /var/www/ghost/config.production.json

# Check failed login attempts
echo "Failed SSH Logins (last 24h):"
journalctl -u sshd --since "24 hours ago" | grep "Failed password" | wc -l

# Check running services
echo "Open Ports:"
sudo netstat -tulpn | grep LISTEN

# Check for available updates
echo "Available Updates:"
sudo apt list --upgradable 2>/dev/null

Common Mistakes

  1. Running Ghost as root: Installing and running Ghost as the root user means any vulnerability in Ghost gives full system access to the attacker. Always create a dedicated ghost user and run Ghost under that user.

  2. Using default SSH configuration: Leaving SSH on port 22 with password authentication enabled invites brute force attacks. Change the port, disable password auth, and use SSH keys only.

  3. Disabling SSL or using self-signed certificates: Without SSL, all traffic (including admin login credentials and member data) is sent in plain text. Use Let's Encrypt for free, trusted certificates.

  4. Not restricting admin panel access: The Ghost admin is accessible at /ghost/ from any IP by default. If you manage the site from fixed IPs, restrict admin access to those IPs via Nginx firewall rules.

  5. Using weak database passwords: Default or guessable database passwords (ghost, password, 12345678) are easily cracked if an attacker gains file access. Use a randomly generated 32-character password.

  6. Skipping regular security updates: Ghost releases security patches for a reason. Running outdated versions means known vulnerabilities are unpatched. Apply security updates within 48 hours of release.

Practice Questions

  1. What are the key security layers for a Ghost production site? Answer: System hardening (file permissions, SSH config, automatic updates), SSL/TLS encryption, UFW firewall, fail2ban for brute force protection, admin panel security (2FA, IP restriction), Nginx security headers, database security (credentials, encrypted backups), and regular security audits.

  2. How does fail2ban protect the Ghost admin panel? Answer: Fail2ban monitors Nginx access logs for failed POST requests to /ghost/api/admin/session/. After a configurable number of failed attempts (e.g., 10 within 5 minutes), it adds an iptables rule to block that IP address for a specified duration (e.g., 1 hour).

  3. What is the recommended configuration for Ghost file permissions? Answer: Ghost directory owned by ghost_user, content directory writable (755), config.production.json restricted to owner read/write (600), and core files read-only (644). No files should be writable by the www-data user or world.

  4. Challenge: Perform a full security audit of a Ghost installation. Check for: SSL configuration and expiry, file permissions, firewall rules, fail2ban status, admin panel access controls, database credentials strength, SSH configuration, and available security updates. Create a report with findings and recommended fixes for each issue.

FAQ

Does Ghost have built-in DDoS protection?

Ghost does not have built-in DDoS protection. For DDoS mitigation, use Cloudflare (free plan includes basic DDoS protection) or a dedicated DDoS protection service. Nginx can be configured with rate limiting to mitigate smaller attacks.

Can I change the Ghost admin URL from /ghost/ to something else?

Ghost does not support changing the admin path. However, you can use Nginx to restrict /ghost/ to specific IP addresses or add an additional authentication layer via Nginx. The underlying Ghost process always serves the admin at /ghost/.

How do I audit failed login attempts in Ghost?

Ghost logs failed login attempts to its own logs (content/logs/). Additionally, Nginx access logs show POST requests to /ghost/api/admin/session/. Use fail2ban to monitor these logs. Ghost does not have a built-in login audit dashboard.

What should I do if my Ghost site is hacked?

Immediately take the site offline. Restore from the most recent clean backup. Audit the backup to ensure it does not contain the vulnerability. Change all passwords (administrator, database, SSH). Apply the security patch that the attacker exploited. Re-deploy from the clean backup.

Is it safe to use Ghost(Pro) instead of self-hosting for security?

Ghost(Pro) handles server-level security (OS updates, firewall, DDoS protection, SSL) for you. You still need to manage account security (strong passwords, 2FA). For teams without dedicated security expertise, Ghost(Pro) reduces the attack surface by eliminating server management responsibilities.

Does Ghost support Web Application Firewall (WAF) integration?

Ghost does not provide a built-in WAF. You can place a WAF in front of Ghost using Cloudflare (WAF available on paid plans), AWS WAF, or ModSecurity as an Nginx module. The WAF inspects traffic before it reaches Ghost and blocks malicious requests.

Mini Project

Your task: Harden a Ghost installation against common attacks.

  1. Set up UFW firewall with restricted access (SSH on custom port, only HTTP/HTTPS open).
  2. Configure fail2ban with custom rules for Ghost admin login protection.
  3. Set up Let's Encrypt SSL with auto-renewal and HSTS.
  4. Restrict Ghost admin panel to trusted IPs via Nginx.
  5. Enable 2FA on all admin accounts.
  6. Set up automatic security updates.
  7. Create an encrypted backup system with GPG.
  8. Write a monthly security audit script.
  9. Document all security configurations in a security runbook.

This exercise gives you a production-ready Ghost security setup.

What's Next

Now that your Ghost site is secured, learn about monitoring and logging:

Continue to Lesson 39: Monitoring & Logging — Track site health, diagnose issues, and set up alerts.

Related lessons:

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro