ERR_HTTP_HEADERS_SENT

Fix Express.js 'ERR_HTTP_HEADERS_SENT' — Stop double responses

Programming & Dev Tools Intermediate 👁 1 views 📅 May 29, 2026

Response sent twice crashes Express. One route handler triggers res.send() or res.json() then hits another. Use `return` or guard early.

Quick answer

Add return before every res.send(), res.json(), res.end(), or res.redirect() call in your route handlers.

return res.status(200).json({ ok: true });

What's actually happening here

Express.js uses Node's built-in HTTP response object. Once you call res.send() or res.json(), the headers get sent to the client immediately. If your code keeps running after that—maybe through an if/else block or a callback chain—and hits another res.send(), you get the ERR_HTTP_HEADERS_SENT error. The full error message reads: Cannot set headers after they are sent to the client.

This usually happens in four scenarios:

  • Multiple conditional branches both send a response without a guard (like else or return).
  • An async callback or promise resolution fires after a synchronous response.
  • Middleware calls next() after sending a response.
  • You accidentally call res.send() twice in the same function (e.g., from a loop).

The error is not an Express bug—it's your code continuing to execute after you've already closed the response. Node's HTTP module locks the socket after headers are sent. Trying to write more headers throws a TypeError that Express doesn't swallow.

Fix steps (in order of effectiveness)

  1. Add return before response calls. This is the most reliable fix.
    app.get('/user/:id', (req, res) => {
    if (!req.params.id) {
    return res.status(400).json({ error: 'Missing ID' });
    }
    return res.json({ id: req.params.id });
    });
    The return stops the function dead. No more code runs.
  2. Wrap conditionals with else blocks.
    if (err) {
    res.status(500).send('Error');
    } else {
    res.send('Success');
    }
    This ensures only one branch executes.
  3. Check if headers are already sent. Use res.headersSent as a guard in rare cases where you can't refactor.
    if (!res.headersSent) {
    res.send('Final response');
    }
    But don't rely on this as your main strategy—it hides the real bug.
  4. Avoid calling next() after sending a response. If you're in middleware that sends a response (like auth), don't call next(). Just return after sending.
    app.use((req, res, next) => {
    if (!req.token) {
    return res.status(401).send('Unauthorized');
    }
    next();
    });

Alternative fixes if the main one fails

Sometimes the error persists even after adding return. Here's what's actually happening then:

  • Async callbacks firing late. You attached a listener or callback that fires after the initial response. For example, an event listener on a stream that calls res.write() or res.end() after the HTTP request already completed. Fix: remove the listener or guard with res.finished or res.destroyed.
  • Promises that resolve after the response. then() blocks that try to send data after the route already returned. The real fix here is to move all async work before sending the response, or use await properly.
  • Express error handlers that re-send. If you have a custom error handler and also call res.send() in the route, the error handler fires too. Use return next(err) in routes to let the error handler be the single response point.

Prevention tip

Adopt a strict pattern in your Express handlers: one response per route, and always return it. I write all my handlers like this:

app.get('/data', async (req, res) => {
try {
const data = await fetchData();
return res.json(data);
} catch (err) {
return res.status(500).json({ error: err.message });
}
});

Notice: every res call has a return. No else blocks needed. The try/catch ensures only one code path sends a response. This pattern eliminates the error entirely.

Also, enable Express's 'error' event listener to catch unhandled errors that might cause double sends:

process.on('unhandledRejection', (err) => {
console.error('Unhandled Rejection:', err);
});

Finally, if you're using a linter like ESLint, add the no-return-await rule and ensure your async functions don't return res.send() values improperly. The error is 100% preventable once you internalize this rule: after you send, you stop.

Was this solution helpful?