Crosspost from StackOverflow

I've looked at multiple other questions about this warning (Headers already sent...etc) and this thorough explanation, but I'm not finding a solution for the circumstances I'm dealing with:

  1. I have a custom Wordpress REST API endpoint that returns the output of get_template_part. When I just call get_template_part normally on a page, there's no warning. It only appears when it runs in register_rest_route's callback.
  2. I am also fetching data from an external API using this library. The error started when I started making these requests. I've tried making the request both inside and outside the template part, the latter either within register_rest_route's callback or as an init action.
  3. The line that triggers the error is an img tag, where I'm echo-ing a URL as the src using data from the external API response. There are other echo calls all over this template, so I doubt that's the issue.
  4. The line in question actually works fine and does its job. I just need to get rid of the accursed warning.


Inside functions.php:


  //the api endpoint declaration
  add_action( 'rest_api_init', function () {
        'theme/v2',   // namespace
        '/homepage',  // route
        array(                  // options
            'methods'  => 'GET',
            'callback' => 'build_home',

  //the callback for the wordpress api
  function build_home() {

    // this is the external API request, using their PHP library. 
    // I linked the library above. The request returns an object with a bunch of data
    $arena = new Arena();
    $page = $arena->set_page(); 
    $per = 8; 
    $slug = 'concepts-bo-6ajlvpqg'; 
    $channel = $arena->get_channel($slug, array('page' => $page, 'per' => $per));

    //I'm passing the response from the external API (in $channel) to get_template part
    //get_template_part is then the output for the wordpress API request


Inside template part:


  <div id="concepts-box" class="bodycontent reg-width">
    <?php foreach ($channel->contents as $item) { if($item->class=="Image"){
    <div class="concept-wrapper">
      <!-- the line below is the one that triggers the error -->
      <img lozad src="<?=$img; ?>">
    <?php }} ?>

Full warning:

<b>Warning</b>:  Cannot modify header information - headers already sent by (output started at /filepath/wp-content/themes/theme/custom-home-template.php:63) in <b>/filepath/wp-includes/rest-api/class-wp-rest-server.php</b> on line <b>1372</b><br />
  • @Nicolai This troubleshooting article suggests the issue is due to spaces/newlines before <?php or after ?>. I don't think this is the case here, not only because I don't see either of those issues, but because the issue doesn't occur when I call get_template_part normally in PHP.
    – Nico
    Jan 19, 2021 at 6:57
  • Are there any other errors? Sometimes errors themselves can result in triggering this second error message. Also try removing the 'null' from your get_template_part(). Jan 19, 2021 at 13:56
  • no other errors — I'm going to implement the output buffering and permission callback suggested by @SallyCJ and see if they make a difference with the warning I'm seeing
    – Nico
    Jan 19, 2021 at 19:01

1 Answer 1


I have a custom Wordpress REST API endpoint that returns the output of get_template_part

Note that the REST API expects your endpoint callback to always return the response (e.g. the template part output in your case), and not echo it or anything else. And after the callback is called, the REST API will send some headers, by default an Allow header and then any extra headers from your response — see WP_REST_Server::serve_request(), particularly lines #440 and #472.

So because of that, you should not echo anything in your callback, because for example echo 'foo'; header( 'Allow: GET' ); will cause the "headers already sent" error because the echo started the output, hence PHP issues a warning if you try to send a (HTTP) header afterwards.

Therefore, despite you said, "The error started when I started making these requests.", I'm pretty sure the problem is with your call to get_template_part() which (despite the name) actually simply loads the template part and if successful, the function returns nothing.

And if the template part echo something (which is what happens in most cases) just like yours as in the question, then that will result in the "headers already sent" warning. Try commenting out the get_template_part() call and see if the warning goes away. :)

But even if not, please ensure your callback is not echoing anything because doing so will invalidate the JSON response (both header and content) which is the default response type returned by the REST API endpoint. However, if you need to echo something, then you would want to use output buffering like so in your case:

function build_home() {

    // ... your code.
    get_template_part( ... your code ... );
    $data = ob_get_clean();

    // The array key ("html") below can be any other name that you prefer.
    return new WP_REST_Response( array(
        'html' => $data,
    ) );

And please always set the permission_callback for your endpoint, because otherwise, you'd get a (_doing_it_wrong()) notice like so:

The REST API route definition for theme/v2/homepage is missing the required permission_callback argument. For REST API routes that are intended to be public, use __return_true as the permission callback.

So for example, your code would look like so:

    'theme/v2',  // namespace
    '/homepage', // route
    array(       // options
        'methods'             => 'GET',
        'callback'            => 'build_home',
        'permission_callback' => '__return_true', // always set a permission callback

And BTW, if you want a different response type (i.e. Content-Type) for your endpoint, then you can use the rest_pre_serve_request hook — see an example in my answer here.

  • I changed the callback to use output buffering and WP_REST_Response, and added permission_callback, and the warning is gone. Thank you! Feel free to duplicate this answer on my original StackOverflow post, and I'll give it to you
    – Nico
    Jan 19, 2021 at 19:11
  • Glad it helped! But I think you can self-answer your question on SO, or maybe just link to this answer? However, I do hope you can upvote this answer.. and even if the vote is not immediately recorded, it will be automatically recorded once you have enough reputation points on this site. :)
    – Sally CJ
    Jan 20, 2021 at 4:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.