Docs/WordPress Security

WordPress Security

Secure your WordPress site by taking it offline while using PhantomWP as a headless CMS.

WordPress Security

When using PhantomWP with WordPress as a headless CMS, you have the option to completely block public access to your WordPress installation. This protects against:

  • Bots and scrapers accessing your content
  • Vulnerabilities in WordPress plugins or themes
  • Direct access to potentially malicious PHP files
  • Brute force attacks on wp-login.php
đź’ˇ

This is completely optional. PhantomWP works fine with a public WordPress site. Only set this up if you want maximum security.

How It Works

PhantomWP uses a secret header (X-PhantomWP-Secret) for all requests to your WordPress REST API. By configuring your server to block requests without this header, you effectively take your WordPress site offline while still allowing PhantomWP to access your content.

Your Server / Cloudflare
Incoming Request:
├─Has X-PhantomWP-Secret header?→ALLOW ✓
└─No header?→BLOCK 403 ✗
PhantomWP
(has header)
âś“ Allowed
You (Admin)
(has header)
âś“ Allowed
Everyone Else
(no header)
âś— Blocked

Setup Steps

Step 1: Get Your Secret Key

  1. Open your project in PhantomWP IDE
  2. Click the WordPress icon (or go to WordPress Settings)
  3. Connect to your WordPress site
  4. Expand the Security section
  5. Copy your secret key (starts with pwp_)

Step 2: Configure Your Browser (For Admin Access)

To access your WordPress admin panel (/wp-admin) after blocking public access, you need to add the secret header to your browser.

Install ModHeader Extension:

Configure ModHeader:

  1. Click the ModHeader icon in your browser
  2. Add a new header:
    • Name: X-PhantomWP-Secret
    • Value: Your secret key (e.g., pwp_a1b2c3d4...)
  3. (Optional) Add a filter to only apply to your WordPress domain

Now you can browse your WordPress site normally—including wp-admin—and the header will be automatically added.

Step 3: Block Requests Without the Header

Choose one of the following options based on your hosting setup:


If your site uses Cloudflare, this is the most secure option. Requests are blocked at the edge before they ever reach your server.

Create a new Worker:

  1. Go to your Cloudflare dashboard
  2. Navigate to Workers & Pages → Create Worker
  3. Paste this code:
export default {
  async fetch(request, env, ctx) {
    const secret = request.headers.get('X-PhantomWP-Secret');
    const expectedSecret = 'YOUR_SECRET_KEY'; // Replace with your key
    
    if (secret !== expectedSecret) {
      return new Response('Forbidden', { 
        status: 403,
        headers: { 'Content-Type': 'text/plain' }
      });
    }
    
    // Forward the request to your origin
    return fetch(request);
  }
}
  1. Deploy the Worker
  2. Go to your domain settings and add a Route that maps your WordPress domain to this Worker

Option B: Cloudflare Access (Zero Trust)

For enterprise-level security, use Cloudflare Access:

  1. Go to Zero Trust → Access → Applications
  2. Create a new Self-hosted application
  3. Add your WordPress domain
  4. Create a policy:
    • Policy name: PhantomWP Access
    • Action: Bypass
    • Include: Service Token (create one for PhantomWP)
  5. Configure PhantomWP to use the Service Token

This approach also lets you add additional authentication methods (email, Google, etc.) for admin access.


Option C: nginx Configuration

Add this to your nginx server block:

# Block requests without the secret header
set $block_request 1;

# Allow requests with valid secret
if ($http_x_phantomwp_secret = "YOUR_SECRET_KEY") {
    set $block_request 0;
}

# Return 403 for blocked requests
if ($block_request) {
    return 403;
}

Full example with WordPress:

server {
    listen 80;
    server_name your-wordpress-site.com;
    root /var/www/wordpress;
    
    # Security: Block requests without secret header
    set $block_request 1;
    if ($http_x_phantomwp_secret = "YOUR_SECRET_KEY") {
        set $block_request 0;
    }
    if ($block_request) {
        return 403;
    }
    
    # Standard WordPress configuration
    location / {
        try_files $uri $uri/ /index.php?$args;
    }
    
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

After editing, reload nginx:

sudo nginx -t && sudo systemctl reload nginx

Option D: Apache .htaccess

Add this to your WordPress .htaccess file (at the beginning, before the WordPress rules):

# PhantomWP Security - Block requests without secret header
RewriteEngine On
RewriteCond %{HTTP:X-PhantomWP-Secret} !^YOUR_SECRET_KEY$
RewriteRule ^ - [F]

Full .htaccess example:

# PhantomWP Security
RewriteEngine On
RewriteCond %{HTTP:X-PhantomWP-Secret} !^YOUR_SECRET_KEY$
RewriteRule ^ - [F]

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

Verifying Your Setup

After configuring your server, test that blocking is working:

Without header (should be blocked):

curl -I https://your-wordpress-site.com
# Expected: HTTP 403 Forbidden

With header (should work):

curl -I -H "X-PhantomWP-Secret: YOUR_SECRET_KEY" https://your-wordpress-site.com
# Expected: HTTP 200 OK

Troubleshooting

I'm locked out of wp-admin

  1. Make sure ModHeader is installed and enabled
  2. Verify the header name is exactly X-PhantomWP-Secret (case-sensitive)
  3. Check that your secret key matches exactly (no extra spaces)
  4. Try disabling other browser extensions that might interfere

PhantomWP can't connect to WordPress

  1. Verify your WordPress URL is correct in PhantomWP settings
  2. Make sure you saved the configuration (secret is generated on save)
  3. Check your server logs for any errors

I want to allow some paths without the header

Modify your server config to exclude certain paths. For example, in nginx:

# Allow health checks without secret
location /health {
    return 200 'OK';
}

# Block everything else without secret
location / {
    set $block_request 1;
    if ($http_x_phantomwp_secret = "YOUR_SECRET_KEY") {
        set $block_request 0;
    }
    if ($block_request) {
        return 403;
    }
    # ... rest of config
}

Rotating Your Secret Key

If you need to change your secret key:

  1. Generate a new key in PhantomWP WordPress Settings
  2. Update your server configuration with the new key
  3. Update ModHeader in your browser with the new key
  4. Test that everything works before removing the old key
⚠️

Always test the new key before removing the old one from your server config. You can temporarily allow both keys during migration.

Security Best Practices

  1. Use HTTPS - Always use HTTPS for your WordPress site to prevent header interception
  2. Keep WordPress updated - Even with this protection, keep WordPress and plugins updated
  3. Use strong passwords - Your WordPress admin password should still be strong
  4. Regular backups - Maintain regular backups of your WordPress database and files
  5. Monitor access logs - Periodically check your server logs for suspicious activity