Title: status=ERR&amp;code=101
Last modified: April 27, 2026

---

# status=ERR&code=101

 *  Resolved [homepagehelden](https://wordpress.org/support/users/homepagehelden/)
 * (@homepagehelden)
 * [2 weeks, 3 days ago](https://wordpress.org/support/topic/statuserrcode101/)
 * `http_headers_option()` hooked into global `added_option`/`updated_option` redirects
   users away from `wp-admin/update-core.php` (and other admin pages) with `?status
   =ERR&code=101`
 * **Plugin:** HTTP Headers (`http-headers`)
   **Plugin version:** 1.19.4**WordPress:**
   tested on 6.9.x (FSE)**PHP:** 8.2+**Site type:** single-site Summary
 * `http_headers_option()` is bound to the **global** WordPress hooks `added_option`
   and `updated_option` (`http-headers.php:1678–1679`). Those hooks fire on every
   option write in `wp_options`, including options that have nothing to do with 
   this plugin — most notably the update-related site transients (`_site_transient_update_core`,`
   _site_transient_update_plugins`, `_site_transient_update_themes`, `_site_transient_update_translations`).
 * The handler then runs an `option_page`-based nonce check (`http-headers.php:837–
   844`) that has no chance of passing on writes that do not originate from one 
   of the plugin’s own settings forms. The check fails, the handler calls `wp_safe_redirect(...'
   options-general.php?page=http-headers&tab=advanced&status=ERR&code=101'); exit;`,
   and the original request is killed.
 * The most user-visible symptom is that **`wp-admin/update-core.php` becomes unreachable**
   in certain situations: every time it is loaded, WordPress refreshes the update
   site-transients via `update_option()`, the plugin’s hook fires, and the page 
   silently redirects to the HTTP Headers settings screen with `code=101`. Reproduction(
   deterministic)
    1. Single-site WordPress install with HTTP Headers 1.19.4 active.
    2. Run any plugin / theme / core update so the update site-transients are invalidated.(
       Equivalent: hit `update-core.php?force-check=1` once to refresh, then wait until
       the transients expire — the second case is harder to time, the post-update case
       is reliable.)
    3. As an administrator, navigate to `wp-admin/update-core.php`.
 * **Expected:** the Updates screen renders.
 * **Actual:** the browser is redirected to `wp-admin/options-general.php?page=http-
   headers&tab=advanced&status=ERR&code=101` and the Updates screen is never shown.
   Other admin pages mostly continue to work, because they do not happen to write`
   wp_options` rows in their own request lifecycle.
 * Deactivating the plugin and re-activating it appears to “fix” the symptom, but
   only because the just-failed request itself wrote the transients before the redirect,
   so the next visit to `update-core.php` finds them within their TTL and skips 
   the refresh — the underlying bug is unchanged and will resurface as soon as the
   transients expire or are force-refreshed. Root cause
 * `http-headers.php:1674–1679`:
 *     ```wp-block-code
       if ( is_admin() ){
           add_action('admin_menu', 'http_headers_admin_add_page');
           add_action('admin_init', 'http_headers_admin');
           add_filter('pre_update_option', 'http_headers_pre_update_option', 10, 3);
           add_action('added_option', 'http_headers_option');
           add_action('updated_option', 'http_headers_option');
           ...
       }
       ```
   
 * `added_option` and `updated_option` are core WordPress hooks that fire for **
   every** option write — they are not restricted to options owned by this plugin.
   They also fire for site-transient writes on single-site installs, because `set_site_transient()`
   ultimately calls `update_option('_site_transient_<name>', ...)` when the install
   is not multisite.
 * `http-headers.php:831–844`:
 *     ```wp-block-code
       function http_headers_option($option) {
   
           include_once ABSPATH . 'wp-admin/includes/admin.php';
           require_once ABSPATH . WPINC . '/pluggable.php';
   
           $action = '-options';
           if (isset($_POST['option_page'])) {
               $action = sanitize_text_field(wp_unslash($_POST['option_page'])) . '-options';
           }
           if (!isset($_POST['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_wpnonce'])), $action)) {
               wp_safe_redirect(sprintf("%soptions-general.php?page=http-headers&tab=advanced&status=ERR&code=101", get_admin_url()));
               exit;
           }
           ...
       }
       ```
   
 * For any option write that does **not** originate from one of the plugin’s own
   settings forms — i.e. for the vast majority of option writes in any WordPress
   install — `$_POST['_wpnonce']` is either entirely absent (GET requests, REST/
   AJAX, WP-Cron, programmatic `update_option()` calls from core or other plugins)
   or belongs to a completely unrelated nonce action. Either way, `wp_verify_nonce(...,'
   <option_page>-options')` returns false, and the handler unconditionally calls`
   wp_safe_redirect(...); exit;`.
 * The `update-core.php` case is just the most visible manifestation, because that
   screen reliably triggers `update_option('_site_transient_update_*', ...)` calls
   on every load when the transients have expired or been invalidated — which is
   exactly the state they are in immediately after any update. Why this design is
   problematic in general
 * Even setting aside `update-core.php`, hooking `added_option` / `updated_option`
   and then issuing a redirect from inside the handler has wider consequences:
    - WP-Cron, REST endpoints, AJAX callbacks and CLI runs that touch `wp_options`(
      extremely common — caches, transients, scheduled tasks, third-party plugins)
      all hit the same code path. They have no `$_POST['_wpnonce']` and therefore
      all fail the check. In an HTTP context the `wp_safe_redirect(); exit;` aborts
      the request mid-flight; in CLI/cron contexts the `exit` truncates the run.
      None of these contexts are the ones the nonce check is trying to protect against.
    - The Settings API has already validated the nonce _before_ `update_option()`
      is reached on legitimate settings-page submissions. A second verification 
      _after_ the write — and crucially, on _every_ unrelated write too — adds no
      defensive value but causes the breakage described above.
 * Suggested fix
 * At minimum, restrict the handler to options actually owned by this plugin and
   bail out for everything else, before any of the nonce / redirect logic runs. 
   All of this plugin’s options are namespaced with the `hh_` prefix:
 *     ```wp-block-code
        function http_headers_option($option) {
   
       +    // Bail for option writes that don't belong to this plugin.
       +    // added_option / updated_option are global hooks and fire for
       +    // every wp_options write (including site transients on single-site
       +    // installs, e.g. _site_transient_update_core / _update_plugins /
       +    // _update_themes refreshed by wp-admin/update-core.php).
       +    if (strpos($option, 'hh_') !== 0) {
       +        return;
       +    }
       +
            include_once ABSPATH . 'wp-admin/includes/admin.php';
            require_once ABSPATH . WPINC . '/pluggable.php';
   
            $action = '-options';
            ...
        }
       ```
   
 * Better long-term: drop the post-write nonce check entirely and rely on the Settings
   API’s own pre-write nonce verification (`options.php` already validates `<option_page
   >-options` before any registered option is written). Any post-write work that
   is genuinely needed (htaccess rebuild, etc.) should be gated on `$option` belonging
   to this plugin and on the appropriate user capability via `current_user_can()`—
   not on inspecting `$_POST['_wpnonce']` from inside a global hook.
 * Additionally, the `wp_safe_redirect(); exit;` pattern is unsafe inside option-
   write hooks regardless of the gate above: those hooks can fire from cron, REST,
   AJAX and CLI contexts where redirecting is meaningless or harmful. Surfacing 
   errors to the user belongs in the request handler that initiated the form submission(
   e.g. `admin_init` for the plugin’s settings pages), not in `updated_option`. 
   Self-contained repro snippet
 * On a single-site install with the plugin active, log in as admin and load:
 *     ```wp-block-code
       /wp-admin/update-core.php?force-check=1
       ```
   
 * The browser ends up at:
 *     ```wp-block-code
       /wp-admin/options-general.php?page=http-headers&tab=advanced&status=ERR&code=101
       ```
   
 * Replacing the hook bodies with the diff above — even without removing the hooks—
   restores normal navigation without affecting any of the plugin’s own settings
   flows.

Viewing 2 replies - 1 through 2 (of 2 total)

 *  [Marc](https://wordpress.org/support/users/mdxfr/)
 * (@mdxfr)
 * [2 weeks, 3 days ago](https://wordpress.org/support/topic/statuserrcode101/#post-18891681)
 * yep, bug confimed, i’ve paused the rollout, waiting for a fix
 * thx
 *  Plugin Author [Dimitar Ivanov](https://wordpress.org/support/users/zinoui/)
 * (@zinoui)
 * [2 weeks, 3 days ago](https://wordpress.org/support/topic/statuserrcode101/#post-18891704)
 * [@homepagehelden](https://wordpress.org/support/users/homepagehelden/) thank 
   you very much for the detailed report.
   I’ve just released a new version addressing
   the reported issue.

Viewing 2 replies - 1 through 2 (of 2 total)

You must be [logged in](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fwordpress.org%2Fsupport%2Ftopic%2Fstatuserrcode101%2F%3Foutput_format%3Dmd&locale=en_US)
to reply to this topic.

 * ![](https://ps.w.org/http-headers/assets/icon-128x128.png?rev=1413576)
 * [HTTP Headers](https://wordpress.org/plugins/http-headers/)
 * [Frequently Asked Questions](https://wordpress.org/plugins/http-headers/#faq)
 * [Support Threads](https://wordpress.org/support/plugin/http-headers/)
 * [Active Topics](https://wordpress.org/support/plugin/http-headers/active/)
 * [Unresolved Topics](https://wordpress.org/support/plugin/http-headers/unresolved/)
 * [Reviews](https://wordpress.org/support/plugin/http-headers/reviews/)

 * 2 replies
 * 3 participants
 * Last reply from: [Dimitar Ivanov](https://wordpress.org/support/users/zinoui/)
 * Last activity: [2 weeks, 3 days ago](https://wordpress.org/support/topic/statuserrcode101/#post-18891704)
 * Status: resolved