Skip to main content
Back to blog
April 28, 2026|8 min read|Antoine Duno

Ghost CMS Security Guide: Nginx, Headers, and Admin Hardening

Self-hosted Ghost gives you full control over your publication — and full responsibility for its security. Here's the complete hardening guide.

ZeriFlow Team

1,404 words

Ghost CMS Security Guide: Nginx, Headers, and Admin Hardening

Ghost CMS security for self-hosted installations requires you to own the full security stack: your Node.js process, the reverse proxy in front of it (Nginx or Caddy), your SSL configuration, your system updates, and Ghost Admin access controls. Ghost-managed hosting (Ghost Pro) handles most of this for you — this guide focuses on self-hosted installations where these decisions are yours.

Check your site's security right now: Free ZeriFlow scan →

1. Production Configuration: config.production.json

Ghost's behavior in production is controlled by config.production.json (or environment variables). Several settings here directly affect security.

Force HTTPS:

json
{
  "url": "https://yourdomain.com",
  "server": {
    "port": 2368,
    "host": "127.0.0.1"
  }
}

Setting the url to https:// causes Ghost to generate HTTPS URLs throughout the site and enables security-related cookies. Set the host to 127.0.0.1 (localhost) so Ghost only accepts connections from the local machine — the reverse proxy (Nginx/Caddy) is the public-facing entry point.

Trust proxy headers:

If running behind Nginx or Caddy, tell Ghost to trust the proxy's forwarded headers:

json
{
  "trustedProxies": "loopback, uniquelocal"
}

Without this, Ghost may misidentify the client IP in logs and rate limiting calculations.

Privacy settings:

json
{
  "privacy": {
    "useGravatar": false,
    "useRpcPing": false,
    "useStructuredData": true
  }
}

Disabling Gravatar prevents leaking member email hashes to Gravatar's CDN on every page load.


2. Security Headers via Nginx

Nginx is the most common reverse proxy for self-hosted Ghost. Add security headers in your Nginx server block — not in Ghost itself, which has no header configuration UI.

nginx
server {
    listen 443 ssl http2;
    server_name yourdomain.com;

    # SSL configuration (handled by Certbot or manual cert setup)
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;

    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
    add_header Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline' https:; img-src * data:; object-src 'none'" always;

    location / {
        proxy_pass http://127.0.0.1:2368;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Use always in add_header directives to ensure headers are added even on error responses.

Run a free ZeriFlow scan → after applying these headers to verify they're correctly configured and no important headers are missing.


3. Caddy as an Alternative: Zero-Config HTTPS

Caddy is an increasingly popular alternative to Nginx for Ghost deployments. Its primary security advantage is automatic HTTPS — Caddy handles Let's Encrypt certificate provisioning and renewal without any configuration.

A complete Ghost Caddyfile:

yourdomain.com {
    reverse_proxy localhost:2368

    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        X-Frame-Options "SAMEORIGIN"
        X-Content-Type-Options "nosniff"
        Referrer-Policy "strict-origin-when-cross-origin"
        Permissions-Policy "camera=(), microphone=(), geolocation=()"
        Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline' https:; object-src 'none'"
        -Server
        -X-Powered-By
    }
}

The -Server and -X-Powered-By directives remove headers that disclose your server software version — useful for reducing information leakage.


4. Member Authentication Security

Ghost's native Members feature provides subscription and membership functionality with email/password authentication. Several configuration choices affect member security.

Email verification:

Ghost requires email verification for new member sign-ups by default. Ensure this is enabled in Ghost Admin → Settings → Members → Email verification. Skipping verification allows sign-ups with addresses the user doesn't own and complicates GDPR data subject request handling.

Magic links vs passwords:

Ghost supports passwordless authentication via email magic links. Magic links are arguably more secure than passwords for most users (no reuse, no weak passwords) but require trusting the email channel's security. If your members are security-conscious, offer both options and let them choose.

Brute-force protection:

Ghost has built-in rate limiting for authentication endpoints. For additional protection, configure Nginx's limit_req_zone to rate-limit requests to /ghost/api/*/session:

nginx
limit_req_zone $binary_remote_addr zone=ghost_auth:10m rate=5r/m;

location ~* /ghost/api/.*/session {
    limit_req zone=ghost_auth burst=5 nodelay;
    proxy_pass http://127.0.0.1:2368;
}

5. Ghost Admin Security

Ghost Admin (yourdomain.com/ghost) is the content management interface. Access to Ghost Admin means access to all content, member data, and site configuration.

Two-factor authentication:

Ghost 5.x includes optional two-factor authentication for Admin users. Enable it in Ghost Admin → Settings → Your Account → Two-factor authentication. As of 2026, 2FA is per-user opt-in — encourage all staff authors and administrators to enable it.

Restrict Ghost Admin access by IP (Nginx):

If your team accesses Ghost Admin from known IP addresses (or via VPN), restricting Admin to those IPs dramatically reduces the attack surface:

nginx
location /ghost {
    allow 203.0.113.10;   # Your office IP
    allow 198.51.100.5;   # Your home IP
    deny all;
    proxy_pass http://127.0.0.1:2368;
}

Staff account hygiene:

  • Create named accounts for each staff member. Never share credentials.
  • Use the role system (Admin, Editor, Author, Contributor) — give each person the minimum role needed.
  • Deactivate accounts when team members leave. Ghost does not automatically expire unused accounts.

6. System Updates and Automated Security Patching

Self-hosted Ghost requires you to manage updates to Ghost itself and to the underlying system software.

Ghost updates:

bash
# With Ghost CLI
ghost update

# Check current version and available updates
ghost update --check

Subscribe to Ghost's security advisory mailing list (ghost.org/security) or watch the Ghost GitHub repository for security releases. Ghost uses semantic versioning — patch releases (e.g., 5.x.y) often include security fixes.

System-level updates (Ubuntu/Debian):

bash
# Enable unattended security upgrades
sudo apt install unattended-upgrades
sudo dpkg-reconfigure unattended-upgrades

This automatically installs security patches for the OS and system packages (including the Node.js runtime if installed via apt). Review /etc/apt/apt.conf.d/50unattended-upgrades to ensure security updates are enabled.

Node.js: Ghost runs on Node.js. Keep your Node version within the range Ghost officially supports, and update to the latest LTS within that range for security patches.


FAQ

### Q: Does Ghost have a built-in firewall or WAF? A: Ghost itself does not include a WAF. For self-hosted installations, add a WAF at the Nginx level (ModSecurity) or use Cloudflare's WAF in front of your Ghost installation. Ghost Pro (managed hosting) includes Fastly CDN-level DDoS protection.

### Q: How do I force HTTPS redirects in Ghost's Nginx configuration? A: Add a separate server block for port 80 with a return 301 https://$host$request_uri; directive. If you're using Certbot, it typically adds this automatically. Verify the redirect is in place by running a ZeriFlow scan, which checks for HTTP → HTTPS redirect and HSTS header presence.

### Q: What's the safest way to run Ghost on a VPS? A: Create a dedicated non-root system user for Ghost (ghost-cli does this automatically). Run Ghost behind Nginx or Caddy (never expose Ghost's Node.js port directly to the internet). Use a firewall (ufw) to block all ports except 80, 443, and SSH. Enable automatic security updates for the OS. Use SSH key authentication only — disable password authentication.

### Q: Can I use Cloudflare in front of Ghost? A: Yes, and it's recommended for most self-hosted installations. Cloudflare provides DDoS mitigation, a WAF, caching, and analytics. In your Nginx configuration, set set_real_ip_from to Cloudflare's IP ranges to correctly identify client IPs. Configure Cloudflare's SSL mode to "Full (strict)" to verify your origin certificate.

### Q: How do I check which security headers my Ghost site is returning? A: Run a ZeriFlow scan on your domain. It inspects every HTTP security header in the response, checks your TLS/SSL configuration, and reports on DMARC/SPF/DKIM records.


Conclusion

Self-hosted Ghost gives you a clean, fast, modern publishing platform — and full responsibility for its security hardening. The foundation: correct config.production.json with HTTPS URLs and localhost binding, a well-configured Nginx or Caddy proxy with a complete security headers block, Ghost Admin 2FA and IP restriction, and regular updates for Ghost and the underlying OS.

The external scan validates that all your configuration work is producing the right headers and TLS configuration in the actual HTTP responses browsers receive.

Run a free ZeriFlow scan → — 60 seconds, no credit card.

Ready to check your site?

Run a free security scan in 30 seconds.

Related articles

Keep reading