← Back to blog

The WordPress theme editor: what the save button actually does

The SandyWP team 8 min read

The Theme File Editor lives at Appearance > Theme File Editor on a classic theme and at Tools > Theme File Editor on a block theme, and it edits the files of an installed theme in place. When you press "Update File" on a PHP file in the active theme, WordPress writes the file to disk, makes two HTTP requests back to your own site to see whether the site still loads, and restores the old contents if it does not.

That check is narrower than most guides suggest. It runs only when the file is PHP and the theme is the active one, so a style.css edit and an edit to any other installed theme save with no safety net at all.

The order of operations, which is the part that matters

Every published guide describes the editor as "it checks your code before saving". The code in wp_edit_theme_plugin_file() does it the other way around.

Read from wp-admin/includes/file.php in wordpress-develop trunk on 2026-10-13, the sequence is:

  1. Capability check (edit_themes), nonce check (edit-theme_<stylesheet>_<file>), and a validate_file() path check.
  2. $previous_content = file_get_contents( $real_file ), held in memory only.
  3. fopen( $real_file, 'w+' ) and fwrite(). The new code is now live.
  4. wp_opcache_invalidate(), so the next request compiles the new file rather than the cached one.
  5. Only then, and only if the file is PHP in the active theme, the two loopback requests.
  6. If either loopback reports a fatal error, file_put_contents( $real_file, $previous_content ) puts the old code back.

So there is a real window where your broken functions.php is the live one. The loopback timeout is 100 seconds and there are two of them, so in the worst case that window is a little over three minutes of real visitors hitting a white screen.

For a site with traffic, that is the honest reason not to use this editor rather than any abstract "it is bad practice" argument.

When the check does not run at all

The condition in core is if ( $is_active && 'php' === $extension ). $is_active is true when the theme you are editing is either the active stylesheet or its parent template.

What you edit Fatal-error check?
functions.php in the active theme Yes
functions.php in the active child theme's parent Yes
style.css in the active theme No
Any .php file in a theme you are not using No

That last row catches people. The theme dropdown at the top of the editor lets you open the files of any installed theme, and switching it does not activate anything, so an edit there is saved with no check and no rollback.

It is dormant until someone activates that theme, at which point the error appears with no obvious connection to a change made days earlier.

What the two loopback requests are

They are not a syntax parse. WordPress genuinely fetches two of its own URLs and reads the output.

Core generates a random scrape_key and a random scrape_nonce, stores the pair as a 60 second transient, then calls wp_remote_get() on the editor screen itself and, if that passes, on home_url( '/' ). Both URLs carry the key and nonce as query arguments and the request forwards your cookies.

On the receiving end, wp_start_scraping_edited_file_errors() in wp-includes/load.php matches the nonce against the transient and registers a shutdown function. That function prints error_get_last(), wrapped in ###### wp_scraping_result_start:<key> ###### markers, which the editor then parses out of the response body.

Two things follow from that design. The check catches only the error types in core's list (E_ERROR, E_PARSE, E_COMPILE_ERROR and friends), so a logic bug or a notice passes happily.

The second is that the check needs your site to be able to make an HTTP request to itself.

"Unable to communicate back with site", and why the message tells you to use SFTP

If the loopback response does not contain the marker, core does not assume the code is fine. It assumes the worst, reverts the file, and returns this:

Unable to communicate back with site to check for fatal errors, so the PHP change was reverted. You will need to upload your PHP file change by some other means, such as by using SFTP.

Your code was probably correct. The editor reverted it because the verdict was unavailable, which is the right call and a confusing one if you do not know the mechanism.

This is why the editor appears broken on sites behind HTTP Basic auth, a staging password wall, a firewall that blocks requests from the server to its own hostname, or a local hosts-file setup the server itself cannot resolve. Core does forward Basic auth credentials if PHP exposes them in $_SERVER, but nothing else about that list is handled.

Why a theme edit never sends you a Recovery Mode email

If a plugin fatals, WordPress emails the site admin a Recovery Mode link. A fatal introduced through the theme editor does not produce that email during the save, and it is deliberate.

wp_start_scraping_edited_file_errors() defines the WP_SANDBOX_SCRAPING constant for the duration of the loopback request. The first thing WP_Fatal_Error_Handler::handle() does is return early when that constant is set, so no error screen is rendered and no recovery email is queued.

The reason is sound: the editor needs the raw fatal in the response body to report it to you, and Recovery Mode would swallow it. The consequence is that if the revert itself fails, you are on your own.

Recovery Mode is still there for real requests afterwards, and it can pause a broken theme the same way it pauses a broken plugin. See our walkthrough of Recovery Mode for how to get into it when the email does not arrive.

Three ways the editor is already disabled, and how to tell them apart

The edit_themes, edit_plugins and edit_files capabilities are all mapped in wp-includes/capabilities.php, and all three are denied if any of the following is true:

  • DISALLOW_FILE_EDIT is defined as true in wp-config.php. This is the one every hardening guide gives you.
  • DISALLOW_FILE_MODS is defined as true. It is checked through wp_is_file_mod_allowed( 'capability_edit_themes' ), so turning off all file modifications turns off the editor as a side effect, and also removes the plugin and theme installers.
  • The site is multisite and you are not a super admin. The capability is denied regardless of your role.

If the menu item is missing and you cannot work out why, check in that order. A managed host will usually have set one of the two constants for you.

A useful detail: DISALLOW_FILE_MODS already implies DISALLOW_FILE_EDIT in core, so writing both is redundant for behaviour but worth doing anyway for whoever reads your wp-config.php next.

What to use instead

If the goal is a CSS tweak, the Additional CSS panel in the Customizer or a block theme's global styles survive theme updates. The editor does not, because your edit lives in the theme directory and the next update overwrites it.

If the goal is a PHP change, put it in a child theme or a small plugin, keep it in git, and deploy it. Our notes on mu-plugins cover the case where you want code that cannot be deactivated by accident.

If the goal is to read a theme file to understand it, the editor is genuinely fine and the fastest tool for it. Nothing above applies to opening a file and not saving it.

Where a sandbox fits, and where it does not

The editor's problem is not the editing, it is that the only copy of the file is the live one. A throwaway WordPress install changes that without changing your habits: you get the same wp-admin, the same Theme File Editor, and a site nobody is reading.

Clone your live site into a sandbox, break functions.php there, watch what the check does and does not catch, then write the fix properly. Every sandbox also has SSH and SFTP with WP-CLI preinstalled, so when the editor reverts your change you can put it back from the shell instead of fighting the loopback.

Two honest limits. A sandbox is not a substitute for version control, so the real fix is still a child theme in a repository rather than a nicer place to click Update File.

And if you are debugging a problem that only happens under your production traffic or your production CDN, the sandbox will not reproduce it. That is a staging-on-your-own-host job, and it is worth saying so.

One SandyWP specific worth knowing if you share sandboxes: a sandbox you create for yourself keeps a full-powered wp-admin, including the editor. A sandbox launched from a public Template link is a protected demo by default, and provisioning writes both DISALLOW_FILE_MODS and DISALLOW_FILE_EDIT into its wp-config.php, so visitors get wp-admin without the file editor or the installers.

The short version

The Theme File Editor writes first and checks second, and it checks only PHP files in the active theme.

The check is two real HTTP requests to your own site, so it fails on sites that cannot reach themselves, and a failed check reverts correct code. No Recovery Mode email is sent during the save because core deliberately switches the fatal error handler off for those requests.

Use it to read files. For anything you intend to keep, use a child theme, a repository, and somewhere disposable to try it first.