DISALLOW_FILE_EDIT vs DISALLOW_FILE_MODS: What Each Actually Stops

DISALLOW_FILE_EDIT vs DISALLOW_FILE_MODS: What Each Actually Stops

Every WordPress hardening checklist tells you to put DISALLOW_FILE_EDIT in wp-config.php, and about half of them describe it as if it locks your filesystem against attackers. It does not. DISALLOW_FILE_EDIT only turns off the built-in code editor screens for themes and plugins in wp-admin; an administrator can still upload a malicious plugin ZIP three clicks later and run whatever PHP they like. The constant that actually stops plugin installs, theme installs and dashboard updates is DISALLOW_FILE_MODS — and turning that one on without understanding the side effects will silently stop your automatic security updates too.

The short answer, if that is all you came for

Set define( 'DISALLOW_FILE_EDIT', true ); on almost every production site: it removes the edit_files, edit_plugins and edit_themes capabilities so nobody can edit PHP live in a browser textarea. Only set define( 'DISALLOW_FILE_MODS', true ); when every core, plugin, theme and translation update is deployed from Git or CI — because it blocks all dashboard installs and updates, including background core security updates.

DISALLOW_FILE_EDIT vs DISALLOW_FILE_MODS: side by side

Both constants live in wp-config.php and both are checked inside map_meta_cap() in wp-includes/capabilities.php. When either constant is truthy, WordPress maps a set of file-related capabilities to do_not_allow for every user on the site, including Super Admins on Multisite.

Action in WordPressDefault (neither set)DISALLOW_FILE_EDIT = trueDISALLOW_FILE_MODS = true
Edit plugin or theme PHP files in wp-admin (edit_plugins, edit_themes, edit_files)Allowed for AdministratorsBlockedBlocked (implies file edit block)
Install or upload a new plugin or theme ZIP (install_plugins, install_themes)Allowed for AdministratorsAllowedBlocked
Update plugins, themes or core from the dashboard (update_plugins, update_themes, update_core)Allowed for AdministratorsAllowedBlocked
Delete an inactive plugin or theme (delete_plugins, delete_themes)Allowed for AdministratorsAllowedBlocked
Activate or deactivate an already-installed plugin (activate_plugins)Allowed for AdministratorsAllowedAllowed
Edit block templates and global styles in the Site Editor (edit_theme_options)Allowed for AdministratorsAllowed (stored in the database, not theme files)Allowed
Automatic background core, plugin, theme and language updatesRuns normallyRuns normallyBlocked via wp_is_file_mod_allowed()
Media Library uploads (upload_files)Allowed for Authors and aboveAllowedAllowed
Capability mappings per map_meta_cap() and wp_is_file_mod_allowed() in WordPress core.

Read the second row carefully. With DISALLOW_FILE_EDIT set to true, the Tools → Theme File Editor and Tools → Plugin File Editor menu items disappear, and direct requests to theme-editor.php or plugin-editor.php die with a permissions message. Everything under Plugins → Add New Plugin, including the Upload Plugin button, still works as normal.

What DISALLOW_FILE_EDIT actually protects you from

If an attacker with a stolen administrator session can still upload a two-line plugin ZIP under DISALLOW_FILE_EDIT, why bother setting it at all? Three reasons, and the most useful one has nothing to do with hackers.

It stops live cowboy edits on production. Somebody pastes a snippet into functions.php through the browser, misses a semicolon, or edits a plugin file that gets overwritten the next morning when an update runs. Removing the editor forces changes through a proper file deploy or a child theme.
It closes the fastest post-compromise foothold. Many automated bots that hijack an admin cookie or exploit an XSS vulnerability look straight for plugin-editor.php and theme-editor.php to append a backdoor into an existing file without changing the plugin list or triggering an install notification.
It costs nothing on a modern block theme. On a classic theme, site owners sometimes used the Theme File Editor to tweak template markup. On a block theme, template edits happen in the Site Editor and save to the database as wp_template posts — customizing a block theme with theme.json or a child theme covers where code changes belong instead.

Understand that boundary before evaluating security plugins that advertise “one-click file editor hardening” as a headline feature. All they are doing is writing that one line into wp-config.php or filtering map_meta_cap at runtime — comparing WooCommerce security plugins separates those checkbox features from controls that actually stop an intrusion.

What DISALLOW_FILE_MODS stops — and what it quietly breaks

DISALLOW_FILE_MODS is a completely different commitment. In core, wp_is_file_mod_allowed( $context ) returns ! DISALLOW_FILE_MODS before running through the file_mod_allowed filter. Core checks that function in four places that matter every day:

  1. Capability mapping (disallow_file_mods context): removes edit_files, edit_plugins, edit_themes, update_plugins, delete_plugins, install_plugins, upload_plugins, update_themes, delete_themes, install_themes, upload_themes, update_core and install_languages.
  2. Automatic updater (automatic_updater context): WP_Automatic_Updater::is_disabled() returns true immediately when file mods are disallowed. No minor core security release, plugin auto-update or theme auto-update will run in the background.
  3. Language pack updates (async_upgrader and can_install_language_pack contexts): translation files cannot be downloaded or updated automatically.
  4. Update notices in wp-admin: because no user has update_core, update_plugins or update_themes, WordPress hides the update badges and rows across the dashboard for everyone.

The trap that catches unmanaged sites

Look at points two and four together. If you paste define( 'DISALLOW_FILE_MODS', true ); into a site that does not have an external deployment pipeline applying updates every week, two things happen at once: background security updates stop running, and the dashboard stops telling you that anything is out of date. When WordPress 7.1.2 patched a critical CVE, a site with DISALLOW_FILE_MODS enabled and no CI job neither applied the patch nor showed a banner in wp-admin. That is the exact same silent-drift failure mode as running a commercial plugin with an expired license key, except you did it to your entire install.

How to set and verify both constants safely

Always place the constants in wp-config.php above the /* That's all, stop editing! Happy publishing. */ line. Placing them at the bottom of the file, after require_once ABSPATH . 'wp-settings.php';, is the most common reason they appear to do nothing — by the time wp-settings.php has loaded, core has already defined its defaults.

1. Set the constant with WP-CLI

wp config reads and writes wp-config.php in the right position and uses --raw so true is written as a boolean literal rather than the string 'true':

# Check what is currently defined in wp-config.php
wp config list DISALLOW_FILE_EDIT DISALLOW_FILE_MODS

# Standard production setting: disable the code editors only
wp config set DISALLOW_FILE_EDIT true --raw --type=constant

# Immutable / Git-deployed production setting: block all file modifications
wp config set DISALLOW_FILE_MODS true --raw --type=constant

2. Verify effective capabilities and whether a plugin is overriding you

Checking wp-config.php alone is not enough, because a security plugin or mu-plugin can define the constant earlier or hook into file_mod_allowed and map_meta_cap. Test the actual runtime result for an administrator account (user ID 1 here):

# Evaluate the runtime constants and core helper
wp eval '
  printf( "DISALLOW_FILE_EDIT: %s\n", defined("DISALLOW_FILE_EDIT") && DISALLOW_FILE_EDIT ? "true" : "false" );
  printf( "DISALLOW_FILE_MODS: %s\n", defined("DISALLOW_FILE_MODS") && DISALLOW_FILE_MODS ? "true" : "false" );
  printf( "file_mod_allowed:   %s\n", wp_is_file_mod_allowed("disallow_file_mods") ? "true" : "false" );
'

# Confirm what user 1 is actually allowed to do
wp user can 1 edit_plugins      # Expect: no  (when either constant is true)
wp user can 1 install_plugins   # Expect: yes (under DISALLOW_FILE_EDIT), no (under DISALLOW_FILE_MODS)

Note an important quirk when you manage a site with DISALLOW_FILE_MODS enabled from the shell: wp plugin install and wp plugin update run without a logged-in user by default, so WP-CLI can still install and update plugins on the server even when DISALLOW_FILE_MODS is true — provided the OS user running wp has filesystem write permissions. If you pass --user=1 to a command that checks capabilities, or if you check Tools → Site Health, WordPress evaluates the capability layer instead. Using WordPress Site Health to find and fix common problems explains what its background-updates test reports when file modifications are turned off.

3. Allow background security updates while keeping dashboard installs locked

Because wp_is_file_mod_allowed() passes a $context string to the file_mod_allowed filter, you can lock down the dashboard with DISALLOW_FILE_MODS while explicitly exempting the automatic updater in an mu-plugin:

<?php
/**
 * Plugin Name: Allow Automatic Updates Under DISALLOW_FILE_MODS
 * Description: Blocks dashboard installs and edits, but lets core's background updater run.
 */

add_filter( 'file_mod_allowed', function ( $file_mod_allowed, $context ) {
	if ( 'automatic_updater' === $context ) {
		return true;
	}

	return $file_mod_allowed;
}, 10, 2 );

Only use that pattern when the filesystem itself is writable by PHP and your goal is purely to stop human admins from installing plugins on production. If your deployment process rsyncs the wp-content/plugins/ directory from staging or Git with --delete — the workflow covered in pushing staging to live in WordPress without losing orders — any background update written directly to production will be wiped out on your next deploy anyway. Keep updates in one place, not two.

Which setting belongs on which environment

  • Standard production site (updated from wp-admin or automatic updates): set DISALLOW_FILE_EDIT to true; leave DISALLOW_FILE_MODS unset or false. Pair it with a safe plugin update workflow and disabling XML-RPC properly.
  • Git-deployed or CI/CD production site: set DISALLOW_FILE_MODS to true on production so nobody installs a plugin that is missing from version control. Apply updates on staging, test them, and deploy the files.
  • Client handoff site where the agency retains maintenance: DISALLOW_FILE_EDIT to true is non-negotiable. Set DISALLOW_FILE_MODS to true only if you are under contract to run updates on a schedule yourself.
  • Staging or local development environment: leave DISALLOW_FILE_MODS off so you can test upgrades freely, and keep DISALLOW_FILE_EDIT on so you are never tempted to edit files somewhere your Git repository cannot see.

If a bad edit or update ever slips through before you lock this down and locks you out of wp-admin, neither constant prevents you from recovering from the shell or SFTP — disabling a WordPress plugin when you cannot access your dashboard and enabling WordPress debugging to read the error log will get you back in within a couple of minutes.

Frequently asked questions

If I set DISALLOW_FILE_MODS to true, do I still need DISALLOW_FILE_EDIT?

No. In map_meta_cap(), edit_files, edit_plugins and edit_themes return do_not_allow if either DISALLOW_FILE_EDIT is true or wp_is_file_mod_allowed( 'disallow_file_mods' ) is false. Setting DISALLOW_FILE_MODS to true automatically disables the file editors too.

Does DISALLOW_FILE_MODS stop users from uploading images to the Media Library?

No. Media uploads are controlled by the upload_files capability and write to wp-content/uploads/. DISALLOW_FILE_MODS only restricts upload_plugins and upload_themes along with code updates and deletions.

Does DISALLOW_FILE_EDIT or DISALLOW_FILE_MODS block the Full Site Editor?

Neither blocks the Site Editor. Editing templates, template parts, patterns and global styles in a block theme checks edit_theme_options and writes to the database (wp_posts), not to PHP or HTML files on disk.

Why is the Plugin File Editor still gone after I removed DISALLOW_FILE_EDIT from wp-config.php?

Either DISALLOW_FILE_MODS is also set to true, a managed host defines DISALLOW_FILE_EDIT in an earlier preload file, or a security plugin is defining the constant or filtering map_meta_cap at runtime. Run wp eval 'var_dump( defined("DISALLOW_FILE_EDIT"), wp_is_file_mod_allowed("disallow_file_mods") );' to see which layer is active.

Can I still activate and deactivate installed plugins when DISALLOW_FILE_MODS is true?

Yes. Activating or deactivating a plugin checks activate_plugins and updates the active_plugins row in wp_options without modifying files on disk, so DISALLOW_FILE_MODS leaves it enabled. You cannot install a new plugin or delete an inactive one from the dashboard while it is set.

Does WP-CLI work when DISALLOW_FILE_MODS is enabled?

Yes. WP-CLI runs directly against the filesystem as your shell user rather than through the capability check that guards the web admin, so wp core update, wp plugin install and wp plugin update still work unless the directory permissions themselves are read-only at the OS level.

Sources

Leave a Reply to This Post