How to Fix the “Headers Already Sent” Warning in WordPress Admin Development
During WordPress admin development, the headers already sent
warning commonly occurs when a wp_redirect()
function is called after HTTP headers have already been sent. This article explains the root cause of this warning and provides a reliable solution. With a practical code example, we demonstrate how to handle redirects using the admin_init
hook to prevent such issues.
What Causes the “Headers Already Sent” Warning?
When developing backend features in WordPress, you may encounter the “headers already sent” warning. This typically happens when your PHP code sends an HTTP header and then later attempts to send another header using wp_redirect()
for a page redirect.
Under the HTTP protocol, a single request can only have one set of headers. If headers have already been sent to the browser, any attempt to send another set—such as via wp_redirect()
—will fail. PHP issues this warning to help developers identify and resolve the problem.
Although this is not a fatal error and your application may still function, it’s good practice to fix such warnings. As responsible developers, we should aim for clean, warning-free code to ensure reliable execution.
How to Fix the “Headers Already Sent” Issue
The key is to ensure that any wp_redirect()
call occurs before any content or headers have been sent to the browser. One effective approach is to use the admin_init
hook, which runs early in the WordPress admin request lifecycle—before most headers are sent.
Here’s a sample code snippet that safely performs a redirect based on the current page and query parameters:
add_action('admin_init', function () {
if (
isset($_GET['page']) && $_GET['page'] === 'list_serial' &&
isset($_GET['action']) && $_GET['action'] === 'delete'
) {
$ids = isset($_GET['serial']) ? (array)$_GET['serial'] : [];
$sendback = remove_query_arg(
['trashed', 'untrashed', 'deleted', 'locked', 'ids'],
wp_get_referer()
);
wp_redirect(add_query_arg([
'trashed' => count($ids),
'ids' => join('_', $ids),
'locked' => 1
], $sendback));
exit;
}
});
This code is typically used in a custom admin page created using the WP_List_Table
class. It detects a delete operation in the admin interface and redirects the user back to the list page with status parameters after deletion.
The important part is that this logic is placed in the admin_init
hook—before WordPress sends headers or outputs anything to the browser. If you were to put this logic inside the class handling the list rendering (which runs after output starts), the redirect would trigger the “headers already sent” warning.
What About the Frontend?
This problem isn’t limited to the admin area. You may also encounter it on the frontend when using wp_redirect()
after output has started. In that case, the same solution applies: move your redirect logic to a hook that runs early in the frontend lifecycle, such as template_redirect
.
Conclusion
The “headers already sent” warning in WordPress is a common issue when dealing with redirects. The solution is simple: ensure that redirects happen before any output is sent. Use early hooks like admin_init
(for admin pages) or template_redirect
(for frontend pages) to perform redirects safely and avoid warnings. Clean code without warnings is not just aesthetically pleasing—it also leads to more stable and predictable applications.