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.
| Control | Recommendation | Compatibility risk |
|---|---|---|
| Disable dashboard file editing | Start here | Low |
| Block PHP execution in uploads | Start here | Low for standard sites |
| Protect sensitive files | Start here | Low |
| Disable directory listing | Start here | Low |
| XML-RPC | Depends on use | Medium |
| REST API protection | Review endpoints | Medium to high |
| Application Passwords | Disable only if unused | Medium |
| Version hiding and enumeration controls | Optional | Low to medium |
| Content-Security-Policy | Advanced | High |
| HSTS | Test carefully | High if misconfigured |
The official WordPress hardening documentation follows the same basic principle: reduce unnecessary exposure while keeping WordPress maintainable and secure.
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.
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 -IndexesVerify 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.
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 setup | Practical approach |
|---|---|
| No XML-RPC integration | Disable XML-RPC after testing |
| Jetpack or another service depends on XML-RPC | Keep required functionality and disable unnecessary behavior such as pingbacks where possible |
| Legacy remote publishing tool | Keep required access and add rate limiting or firewall controls |
| Not sure | Check 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.
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.
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.
| Header | Starting point | What to watch |
|---|---|---|
X-Content-Type-Options | nosniff | Generally low risk |
Referrer-Policy | strict-origin-when-cross-origin | Workflows that depend on full cross-site referrer URLs |
X-Frame-Options | SAMEORIGIN | Sites intentionally embedded on another domain |
Permissions-Policy | Restrict deliberately | Camera, microphone, location, payment, fullscreen and embedded functionality |
Strict-Transport-Security | HTTPS first | Subdomains and long-term HTTPS availability |
Content-Security-Policy | Report first | Scripts, 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.
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.
| Change | What to verify |
|---|---|
| Dashboard editor disabled | Administrators no longer see the theme or plugin file editors |
| PHP blocked in uploads | A controlled PHP test file cannot execute from uploads |
| Sensitive files protected | Direct browser requests do not expose protected content |
| Directory listing disabled | A directory without an index file does not expose its contents |
| XML-RPC changed | Required integrations still work and unwanted access is restricted |
| REST protection enabled | Required public endpoints still work while unnecessary anonymous routes are restricted |
| Application Passwords disabled | No required API client or publishing workflow loses access |
| Security headers deployed | Expected 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.