WordPress security guide

How to Harden WordPress Without Breaking Your Site

Reduce unnecessary attack surface without blindly disabling WordPress features your site needs. Start with low-risk file and server protections, then review APIs, authentication features, and browser security controls based on how your site actually works.

What to Harden First

WordPress hardening should close attack paths your site does not need without disabling features it actually uses. Start with file and server protections that rarely affect normal operation, then review APIs, authentication features, and browser controls individually.

ControlRecommendationCompatibility risk
Disable dashboard file editingStart hereLow
Block PHP execution in uploadsStart hereLow for standard sites
Protect sensitive filesStart hereLow
Disable directory listingStart hereLow
XML-RPCDepends on useMedium
REST API protectionReview endpointsMedium to high
Application PasswordsDisable only if unusedMedium
Version hiding and enumeration controlsOptionalLow to medium
Content-Security-PolicyAdvancedHigh
HSTSTest carefullyHigh if misconfigured

The official WordPress hardening documentation follows the same basic principle: reduce unnecessary exposure while keeping WordPress maintainable and secure.

Make changes in small groups. Apply a control, test the site, then continue. If checkout, publishing, a form, an API integration, or another feature stops working, you will know which change caused it.

Disable Dashboard File Editing

WordPress administrators can normally edit theme and plugin PHP files from the dashboard. If an administrator account or session is compromised, that editor gives an attacker a convenient way to modify executable code.

On a production site, code changes are better handled through SFTP, SSH, Git, your hosting tools, or a staging and deployment workflow.

Add this to wp-config.php:

define( 'DISALLOW_FILE_EDIT', true );

This removes the Theme File Editor and Plugin File Editor. Normal theme and plugin updates continue to work.

Verify it

Log in as an administrator and confirm the file editor options no longer appear under Appearance or Plugins.

Block PHP Execution in Uploads

wp-content/uploads/ is meant for uploaded media and files. A standard WordPress installation does not need to execute PHP from this directory.

This matters because an upload vulnerability is much more dangerous if an attacker can place a PHP web shell in a writable directory and execute it directly.

Apache or LiteSpeed

Where directory-level .htaccess rules are supported, place a rule in wp-content/uploads/.htaccess that denies PHP-like files:

<FilesMatch "\.(php|php[0-9]|phtml|phar)$">
    Require all denied
</FilesMatch>

Nginx

For Nginx, the protection belongs in the server configuration:

location ~* /(?:uploads|files)/.*\.php$ {
    deny all;
}

WordPress includes this type of restriction in its Nginx configuration guidance.

Verify it

On a staging site or during a controlled test, place a harmless PHP file in uploads and request it directly. The server should deny the request rather than execute the file. Remove the test file afterward.

If this breaks a plugin: investigate why that plugin needs executable PHP inside a writable uploads directory before allowing it again.

Protect Sensitive Files and Directory Access

Configuration files, debug logs, backups, dotfiles, and development artifacts should not be downloadable through the browser.

Protect sensitive files

On Apache 2.4+, a rule can deny browser access to specific files:

<FilesMatch "^(wp-config\.php|\.htaccess|\.htpasswd|debug\.log)$">
    Require all denied
</FilesMatch>

Adapt the list to your environment. Database exports, backup archives, old configuration copies, .env files, version-control metadata, and temporary development files should not be left publicly accessible.

See the official WordPress Apache configuration guidance.

Disable directory listing

If Apache is configured to show directory indexes, disable them unless you intentionally operate a browsable file directory:

Options -Indexes

Verify it

  • Request protected files directly and confirm their contents are not exposed.
  • Check any backup or debug-log location you intentionally protected.
  • Request a directory without an index file and confirm it does not display a file listing.
Using SiteFort: Server Hardening can disable directory listing, block PHP execution in uploads, restrict direct PHP execution in plugin and theme directories, and protect sensitive files. If SiteFort cannot write the server configuration, use its generated manual rules instead. See the Hardening docs for exact server behavior.

Disable or Restrict WordPress Features Only When You Do Not Need Them

XML-RPC, the REST API, and Application Passwords are legitimate WordPress features. Hardening does not mean disabling them automatically. Reduce access only where the site does not need it, and test the functionality that depends on each feature.

XML-RPC

XML-RPC supports older remote-publishing workflows and some integrations. If your site does not use it, reducing or disabling that exposure is reasonable.

Your setupPractical approach
No XML-RPC integrationDisable XML-RPC after testing
Jetpack or another service depends on XML-RPCKeep required functionality and disable unnecessary behavior such as pingbacks where possible
Legacy remote publishing toolKeep required access and add rate limiting or firewall controls
Not sureCheck integrations before disabling it

Be careful with snippets using only the xmlrpc_enabled filter. WordPress documents that this controls XML-RPC methods requiring authentication; it does not disable every XML-RPC method or pingback behavior. WordPress hook reference.

REST API

The WordPress REST API is normal application infrastructure. The Block Editor, WooCommerce, forms, mobile applications, headless frontends, and many plugins depend on REST endpoints.

Do not block /wp-json/ outright as a generic hardening measure. The useful goal is to preserve routes that genuinely need public access, keep WordPress permission checks intact, and restrict unnecessary anonymous access.

Before applying REST restrictions, check whether your site uses:

  • WooCommerce Cart or Checkout Blocks
  • public product, search, or filtering APIs
  • contact, booking, quote, or registration forms
  • page builders or block-based frontend features
  • membership or LMS functionality
  • mobile applications
  • headless WordPress frontends
  • external publishing or automation tools

The WordPress REST API documentation recommends keeping the API available and relying on authentication and permission controls rather than disabling it completely.

How SiteFort handles REST API protection

SiteFort's Restrict REST API Access is endpoint-aware rather than a simple REST API shutdown.

Its Endpoint Status view identifies registered routes using states including:

  • Core Protected for routes WordPress core already protects
  • Core Permissions or a capability name when WordPress permission checks apply
  • Public Allowed for routes intentionally available without authentication
  • Login Required for routes restricted to authenticated users
  • Unrestricted for endpoints that deserve review

Unknown endpoints are restricted for unauthenticated visitors unless they are explicitly allowed. If a theme, form, commerce feature, headless frontend, or integration needs a public route, you can allow that endpoint without opening the entire REST API.

Practical SiteFort rollout: enable REST protection, review Endpoint Status, then test the site while logged out. If a legitimate feature is blocked, identify the exact route it needs and mark that endpoint Allow Public instead of disabling REST protection globally.

Application Passwords

Application Passwords provide separate, revocable credentials for API clients and integrations. They are not used for normal wp-admin login.

If your site uses a mobile app, remote publishing workflow, automation system, or API client, Application Passwords may be legitimate and useful. WordPress stores them as separate credentials and allows individual passwords to be revoked. WordPress Application Passwords documentation.

If you know they are not used, you can disable them:

add_filter( 'wp_is_application_passwords_available', '__return_false' );

Use a small site-specific or must-use plugin for this rather than putting security configuration in a theme.

Verify these changes

Test the site logged out as well as logged in. Submit public forms, place a WooCommerce test order, use live search, check frontend account features, and test any external application connected to WordPress.

If a legitimate integration stops working, identify the exact feature or endpoint it requires rather than rolling back every hardening control.

Reduce Easy Reconnaissance, But Do Not Rely on Obscurity

Hiding WordPress version strings, reducing username enumeration, cleaning unnecessary head metadata, and changing the login URL can reduce easy reconnaissance and generic bot noise.

They are secondary controls. They do not patch vulnerable software, protect a stolen administrator password, stop executable malware, or replace firewall and login protection.

  • Hide WordPress version: removes an easy fingerprint, but updates matter far more.
  • Block user enumeration: makes trivial username discovery harder; check public author and API functionality afterward.
  • Clean WordPress head: removes unnecessary metadata; check feeds or discovery features you use.
  • Custom login URL: can reduce generic requests to wp-login.php, but does not replace 2FA or rate limiting.
Using SiteFort: these controls are available under WordPress Obscurity. Apply them after the higher-value file, execution, access, and authentication protections are in place.

Add Security Headers Carefully

Security headers tell browsers how your site may be loaded, embedded, and interpreted. Some are reasonable low-risk defaults. Others should be deployed only after testing the actual site.

HeaderStarting pointWhat to watch
X-Content-Type-OptionsnosniffGenerally low risk
Referrer-Policystrict-origin-when-cross-originWorkflows that depend on full cross-site referrer URLs
X-Frame-OptionsSAMEORIGINSites intentionally embedded on another domain
Permissions-PolicyRestrict deliberatelyCamera, microphone, location, payment, fullscreen and embedded functionality
Strict-Transport-SecurityHTTPS firstSubdomains and long-term HTTPS availability
Content-Security-PolicyReport firstScripts, styles, payment providers, analytics, maps, ads and embeds

CSP: report before enforcing

A Content Security Policy can reduce what unauthorized browser content is allowed to load, but a generic policy copied from another site can easily break legitimate scripts and integrations. Start in report-only mode, test important frontend flows, review violations, and enforce only after required resources are accounted for.

HSTS: use it only when HTTPS is permanent

HSTS tells browsers to use HTTPS for a defined period. Be especially careful with includeSubDomains and preload because they can affect hostnames beyond the WordPress installation and are not easy to reverse immediately.

Verify your headers

curl -I https://example.com/

After deploying CSP or other restrictive policies, test important pages in a browser and review the developer console for blocked resources or broken functionality.

Using SiteFort: run Analyze Your Security Headers first. SiteFort shows headers already deployed by WordPress, your host, or another layer so you do not blindly duplicate them. Start with lower-risk headers and test CSP and HSTS separately. See the Hardening docs for exact configuration options.

Verify Your Hardening Changes

Do not finish with a list of enabled settings. Check that the protections are actually working and that legitimate functionality still works with them in place.

ChangeWhat to verify
Dashboard editor disabledAdministrators no longer see the theme or plugin file editors
PHP blocked in uploadsA controlled PHP test file cannot execute from uploads
Sensitive files protectedDirect browser requests do not expose protected content
Directory listing disabledA directory without an index file does not expose its contents
XML-RPC changedRequired integrations still work and unwanted access is restricted
REST protection enabledRequired public endpoints still work while unnecessary anonymous routes are restricted
Application Passwords disabledNo required API client or publishing workflow loses access
Security headers deployedExpected headers are present and important frontend flows remain functional

For WooCommerce, membership, LMS, booking, or heavily integrated sites, test the full user journey: login, registration, password reset, forms, search, checkout, payment confirmation, account pages, API clients, and third-party integrations.

The safest hardening configuration is the strictest one you have actually tested, not simply the strictest option available.

Using SiteFort: SiteFort can apply and monitor many of these WordPress and server hardening controls from one place. Use the Hardening documentation when you need exact setting behavior, endpoint controls, server compatibility, or generated manual configuration rules.