Connection: close header not sent before closing HTTP 1.1 connection when a controller raises an exception

Open
#6,859 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@javiergarea is already working on this.

Since Oct 5, 2026.

  • #6866 by @javiergarea — open

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
55/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
elixir
Domain
api, backend

Research direction

Start with lib/phoenix/endpoint/render_errors.ex and the linked Phoenix Bandit reproduction, then trace the response through Bandit’s HTTP/1 socket, pipeline, and handler paths named in the report. Run phoenix_bandit_repro.exs.zip and verify that an error response followed by connection closure advertises Connection: close before the socket closes.

Written by the indexing model from the issue text.

Description

Environment
  • Elixir version (elixir -v): 1.20.4
    Erlang/OTP 28 [erts-16.4.0.6] [source] [64-bit] [smp:14:14] [ds:14:14:10] [async-threads:1] [jit]
    Elixir 1.20.4 (compiled with Erlang/OTP 28)

  • Phoenix version (mix deps): 1.8.15

  • Bandit version: 1.12.5

  • Operating system: macOS 26.7.1

Background

While fuzz-testing our HTTP API with ⁠Schemathesis, we found that sequences including syntactically valid HTTP/1.1 requests containing invalid API input, such as GET /api/items?limit=not-a-number that results in a 400 response, sometimes produces a subsequent connection-reuse error.

Actual behavior

When processing a HTTP 1.1 request, raising a Plug.BadRequestError in the controller results in a complete HTTP 400 response, after which the TCP connection is closed without a prior HTTP Connection: close header.

Code sequence
  1. Phoenix renders the error response, then re-raises. Its rendering path does not add Connection: close.
  2. When sending the headers, Bandit selects the HTTP/1.1 keep-alive path.
  3. Bandit catches the re-raised exception and returns an error. Its already-sent response check correctly avoids sending another response, but consequently skips sending a Connection: close.
  4. Bandit’s HTTP/1 handler closes the connection on that error

phoenix_bandit_repro.exs.zip

Expected behavior

The server sends a Connection: close header when the server knows it will close the connection immediately afterward the HTTP response.

RFC 9112 §9.6 says a server SHOULD include the Connection: close header when it intends to close the connection. While this is a SHOULD, not a MUST level item, explicitly advertising closure improves compatibility with conformance testing tools and HTTP clients in the wild.

Dominant language
Elixir
Stars
23.2k
Forks
3.2k
Avg merge
16h 35m
Merged PRs (30d)
18

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from phoenixframework/phoenix

All issues in phoenixframework/phoenix

Similar issues

More Elixir issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.