• Hello!

    I am a total amateur trying to learn PHP and WordPress to develop a plugin to search and edit user information from custom database tables. The way it works is a basic search field that sends a post request using the submitted email. This email is searched and an ID is found and is supposed to be appended to the URL and go to a results page.

    I had everything working great and then I started getting the following error when I try to redirect to the results page:

    [20-Apr-2025 21:58:17 UTC] PHP Warning: Cannot modify header information – headers already sent by (output started at …public/wp-includes/fonts/class-wp-font-face.php:121) in …/public/wp-includes/pluggable.php on line 1450
    [20-Apr-2025 21:58:17 UTC] PHP Warning: Cannot modify header information – headers already sent by (output started at …/public/wp-includes/fonts/class-wp-font-face.php:121) in …./public/wp-includes/pluggable.php on line 1453

    I did not touch wordpress core and even did a fresh install. Can someone let me know if this code contains any of the errors that would cause this:

    function search_processing() {

    if( $_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['email'] )) {

    $this->email = sanitize_email( $_POST['email'] );

    $this->model = new Example_Class( $this->email );

    $this->some_id = $this->model->get_some_id();

    if (!empty( $this->some_id )) {

    $results = add_query_arg(array(

    'some_id' => $this->some_id

    ));

    wp_redirect( $results );

    exit;

    } else {

    $this->error_email = sanitize_email( $_POST['email'] );

    $this->error_message = 'No ID found for ' . $this->error_email . '.';

    return;

    }

    }

    // If we have a some_id in GET parameters show results

    if ( !empty( $_GET['some_id'] ) ) {

    include __DIR__ . '/../views/results.php';

    exit;

    }

    }
Viewing 2 replies - 1 through 2 (of 2 total)
  • This means your script tried to send HTTP headers after something was already output to the browser — usually whitespace, HTML, or even a BOM character (Byte Order Mark).

    Possible Triggers in Your Case:

    Even if you didn’t touch WordPress core, this can happen if:

    • There’s any whitespace or echo before the wp_redirect() call.
    • An included file like results.php or your main plugin file has whitespace outside <?php ... ?>.
    • PHP files use UTF-8 with BOM, which sends output before headers.

    Use ob_start() at the beginning of your file if needed to buffer output:

    Hello @jgdraper328

    That “headers already sent” warning is a very common issue when working with redirects in PHP and WordPress. Let’s break down what’s happening and how to fix it.

    1. What the Error Means

    The error message: PHP Warning: Cannot modify header information – headers already sent by (output started at …public/wp-includes/fonts/class-wp-font-face.php:121) in pluggable.php on line 1450

    • “Cannot modify header information”: PHP needs to send HTTP headers (like the Location: header for a redirect) before any actual content (HTML, whitespace, etc.) is sent to the browser.
    • “headers already sent by (output started at …)”: This tells you exactly where the output began before your redirect attempt. In your case, it’s class-wp-font-face.php on line 121.
    • “…in pluggable.php on line 1450”: This is the line inside WordPress’s wp_redirect function where it tries (and fails) to send the Location: header because output already started.

    2. What’s Causing the Output?

    The file class-wp-font-face.php is part of WordPress core, specifically the Font Face API introduced in WP 6.4. Line 121 in that file is:

    // ...existing code...
    printf( $this->get_style_element(), $css );
    // ...existing code...

    This line prints the <style> block containing @font-face rules into the HTML <head> of your page. This is normal WordPress behavior – it’s setting up fonts for your theme. You didn’t do anything wrong here, and you definitely shouldn’t modify this core file.

    3. Why is it a Problem for Your Code?

    Your search_processing() function attempts to call wp_redirect() after WordPress has already started building the HTML page and printed those font styles. Because output has already started, PHP cannot send the necessary Location: header for the redirect to work.

    4. How to Resolve This

    The key is to ensure your form processing and potential redirect happen before WordPress starts outputting any HTML. The standard WordPress way to handle this is by using an appropriate action hook.

    • Hook Earlier: Instead of running your search_processing() logic directly in a template file or a shortcode handler (which often run after the headers have started), hook it into an action that fires earlier in the WordPress request lifecycle.
      • template_redirect: This hook is specifically designed for actions like intercepting page requests, handling form submissions, and performing redirects before the theme’s template file is loaded. This is usually the best hook for your scenario.
      • init: This hook runs even earlier, but template_redirect is generally preferred for this type of task as conditional tags (like is_page()) are available.
    • Separate Processing and Display: Your search_processing function currently tries to do two things: process the POST request (and potentially redirect) and display the results if a GET parameter is present. You should separate these:
      1. Processing Function (hooked to template_redirect): This function should only check for the POST request ($_SERVER['REQUEST_METHOD'] === 'POST'). If it’s a POST request, it performs the lookup, and if an ID is found, it calls wp_redirect() and exit;. It does nothing if it’s not a POST request or if no ID is found.
      2. Display Logic: Let WordPress handle the display. After the redirect, the browser will load the new URL (e.g., your-page/?some_id=123). You can then display the results on that page. How you display depends on your setup:
        • If it’s a specific WordPress page, you can modify that page’s template to check for $_GET['some_id'] and include your results.php view.
        • If you’re using a shortcode, the shortcode function can check for $_GET['some_id'] and display the results.

    Example Implementation:

    <?php
    // Assuming this code is in your plugin's main file or a class

    // Hook the processing function to template_redirect
    add_action( 'template_redirect', 'my_plugin_handle_search_submission' );

    function my_plugin_handle_search_submission() {
    // Only process if it's a POST request for your specific form
    // (You might add a hidden field to your form to check here too)
    if ( 'POST' === $_SERVER['REQUEST_METHOD'] && ! empty( $_POST['email'] ) ) {

    // Use local variables instead of class properties if this is a standalone function
    $email = sanitize_email( $_POST['email'] );
    // Assuming Example_Class is available or loaded
    $model = new Example_Class( $email );
    $some_id = $model->get_some_id();

    if ( ! empty( $some_id ) ) {
    // Build the URL to redirect to.
    // Replace 'your-results-page-slug' with the actual slug of your results page
    $results_page_url = home_url( '/your-results-page-slug/' );
    $redirect_url = add_query_arg( array( 'some_id' => $some_id ), $results_page_url );

    // Perform the redirect and stop execution
    wp_redirect( $redirect_url );
    exit;

    } else {
    // Handle the "ID not found" case. You might want to redirect back
    // to the search form with an error message query parameter.
    $search_page_url = home_url( '/your-search-page-slug/' ); // Replace with your search page slug
    $redirect_url = add_query_arg( array(
    'search_error' => 'not_found',
    'searched_email' => rawurlencode( $email ) // Pass the email back if needed
    ), $search_page_url );
    wp_redirect( $redirect_url );
    exit;
    }
    }
    // If it's not a POST request for this form, do nothing and let WordPress continue.
    }

    // --- In your results page template OR your shortcode function ---

    // Check if the ID is present in the URL
    if ( ! empty( $_GET['some_id'] ) ) {
    $retrieved_id = absint( $_GET['some_id'] ); // Sanitize the ID

    // Now you have $retrieved_id, you can fetch the data and display it
    // Example:
    // $data_to_display = get_data_by_some_id( $retrieved_id );
    include __DIR__ . '/../views/results.php'; // Make sure $retrieved_id is available in this view

    } elseif ( ! empty( $_GET['search_error'] ) && 'not_found' === $_GET['search_error'] ) {
    // Optionally display the error message on the search page itself
    $error_email = ! empty( $_GET['searched_email'] ) ? sanitize_email( rawurldecode( $_GET['searched_email'] ) ) : '';
    echo '<p class="error-message">No ID found for ' . esc_html( $error_email ) . '.</p>';
    }

    // Your regular search form HTML would go here or be part of the template/shortcode output
    ?>

    By hooking into template_redirect, you ensure your redirect logic runs before any HTML output, resolving the “headers already sent” error.

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

The topic ‘Cannot modify header information warning’ is closed to new replies.