Open DamianEdwards opened 1 year ago
This is happening because of how Nextjs handles incoming packets. By default, Nextjs will start two HTTP servers. One server sits in the main thread that acts like a proxy, and another thread sits in a forked mode, where your main app will be running (See start-server.ts#L240).
I think the devs structured this way to avoid memory leaks. If the memory usage on the forked thread exceeds a certain threshold, the main thread simply restarts the forked thread without killing the main HTTP server.
Now, back to the main question. Why does it put Connection: close
to the header? The problem is how the packets are relayed to the forked thread. Nextjs uses a library called http-proxy
(See start-server.ts#L255) which just proxies incoming request and relays it to another server (in this case the other HTTP server that sits on forked thread). ~Internally the library doesn't handle keepalive connections. Thus, it appends Connection: close
to the header.~ Edit: Nope, the library does support keep-alive connection under some conditions (See node-http-proxy/.../web-outgoing.js#L46).
An alternative solution would be turning off the worker thread, but the devs have not given that an option yet (See next-dev.ts#L220). Typically, Nextjs only handles web server and should not handle anything else, as it will create more complexities on the project. Personally, I would just put a reverse proxy (like nginx) in front of Nextjs server. Thus, making closed TCP connection not much of a big deal.
Perhaps it's related with this issue ? 51605
I read the stack trace and it starts with TLSSocket. And if I am not mistaken, Nextjs doesn't handle TLS/SSL. Furthermore, referring to my former comment, when the main thread relays the request to the forked thread, it uses plain old HTTP.
@NadhifRadityo it's not clear (at least to me) from the code you link to why that's resulting in a connection: close
header. To my understanding, Node will only respond with connection: close
if keep-alive is explicitly disabled by setting server.keepAliveTimeout
to 0
. The keep-alive timeout is accepted as a parameter that defaults to null
and is only set on the server
if it's not null, so 0
would need to be passed in, or some other part of the server logic is causing the issue.
After further digging into the code, I have a small working patch to make the server utilizes keep-alive connections.
Edit the file node_modules/next/dist/server/lib/start-server.js
starting at line 221 to the following patch:
const proxyServer = httpProxy.createProxy({
target: targetUrl,
changeOrigin: false,
ignorePath: true,
xfwd: true,
ws: true,
followRedirects: false,
+ agent: new (require("http")).Agent({
+ keepAlive: true
+ })
});
Explanation: Apparently, my former comment was partially correct. After a bit of experimenting, I realized:
Connection: close
to the response header (and server.keepAliveTimeout
is the node's default value by the documentation you mentioned.)
https://github.com/vercel/next.js/blob/57ab2818b93627e91c937a130fb56a36c41629c3/packages/next/src/server/lib/render-server.ts#L81-L83Connection: close
. Now this is a bit weird. Because the original request specified Connection: keep-alive
, but the server responded otherwise.http-proxy
library explicitly sets Connection: close
to the proxied request. This explains why the server responded Connection: close
on point 2.
https://github.com/http-party/node-http-proxy/blob/9b96cd725127a024dabebec6c7ea8c807272223d/lib/http-proxy/common.js#L62-L74By setting a custom agent, http-proxy
will use that agent to manage live TCP connections. Currently, a manual patch is required, since Nextjs don't have options to manage http-proxy
yet.
Note: The same patch probably also applies to IPC communications between Next's router and Next's actual page renderer. https://github.com/vercel/next.js/blob/57ab2818b93627e91c937a130fb56a36c41629c3/packages/next/src/server/lib/server-ipc/invoke-request.ts#L26-L31
I noticed Next.js changed its request proxying recently. Currently, Next.js uses built-in fetch instead of http-proxy
for basic HTTP request. These changes made the Connection
header as-is, as opposed to the http-proxy
which overrides the header.
In the latest canary version, Connection
header already defaulted to keep-alive
with timeout of 5 seconds (which is the default nodejs http keep-alive timeout).
For reference, please see the following snippets:
https://github.com/vercel/next.js/blob/2b38118ea005c1bf6bc4059534754080cef7af54/packages/next/src/server/lib/router-server.ts#L368-L376 https://github.com/vercel/next.js/blob/2b38118ea005c1bf6bc4059534754080cef7af54/packages/next/src/server/lib/server-ipc/invoke-request.ts#L27-L46
If you think the problem no longer exists, please consider closing this issue. Thank you!
I had same problem with docker based on node:20-bookworm
image and tried node:20-alpine
as well. When I changed to node:19, it fixed the issue. Hope this helps
I had same problem with docker based on
node:20-bookworm
image and triednode:20-alpine
as well. When I changed to node:19, it fixed the issue. Hope this helps
I think there is (or should be) NO correlation between node version 19/20 that made the connection header keep-alive by itself. Since the behaviour is based on Next.js' code handling request. Can you confirm that you were running the same Next.js version?
Though, this issue is actually been resolved by the latest Next.js version, as per my last message.
I think there is (or should be) NO correlation between node version 19/20 that made the connection header keep-alive by itself. Since the behaviour is based on Next.js' code handling request. Can you confirm that you were running the same Next.js version?
Though, this issue is actually been resolved by the latest Next.js version, as per my last message.
I was facing this issue on next@13.4.12
. I tried to install latest nextjs version which was next@13.4.18
. I removed both .next & node_modules folders from my project to avoid any cache or something and I build the project again. The result was same. Then I've become the man who hits every key to pass the level and tried to change nodejs version from docker and it fixed it.
I am facing this error Error: Connection closed. at c
on one specific route when deployed to Vercel. This route works fine in the local build. I used next@13.5.4
and also tried to downgrade to several versions, but none worked.
@autsada had a similar issue
facing a similar issue... using api endpoints with redirect
causes application to throw Error: Connection closed.
⨯ Error: Connection closed.
at tu (/Users/hoarfroster/Desktop/hfrecipe/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:303532)
at t.decodeReply (/Users/hoarfroster/Desktop/hfrecipe/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:35:304915)
at /Users/hoarfroster/Desktop/hfrecipe/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:38:264
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async tX (/Users/hoarfroster/Desktop/hfrecipe/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:37:5207)
at async rl (/Users/hoarfroster/Desktop/hfrecipe/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:38:22994)
at async doRender (/Users/hoarfroster/Desktop/hfrecipe/node_modules/next/dist/server/base-server.js:1407:30)
at async cacheEntry.responseCache.get.routeKind (/Users/hoarfroster/Desktop/hfrecipe/node_modules/next/dist/server/base-server.js:1561:40)
at async DevServer.renderToResponseWithComponentsImpl (/Users/hoarfroster/Desktop/hfrecipe/node_modules/next/dist/server/base-server.js:1479:28)
at async DevServer.renderPageComponent (/Users/hoarfroster/Desktop/hfrecipe/node_modules/next/dist/server/base-server.js:1850:24)
at async DevServer.renderToResponseImpl (/Users/hoarfroster/Desktop/hfrecipe/node_modules/next/dist/server/base-server.js:1888:32)
at async DevServer.pipeImpl (/Users/hoarfroster/Desktop/hfrecipe/node_modules/next/dist/server/base-server.js:902:25)
at async NextNodeServer.handleCatchallRenderRequest (/Users/hoarfroster/Desktop/hfrecipe/node_modules/next/dist/server/next-server.js:266:17)
at async DevServer.handleRequestImpl (/Users/hoarfroster/Desktop/hfrecipe/node_modules/next/dist/server/base-server.js:798:17)
export async function POST(req: Request) {
...some logic
let tmp = new URL(path, req.url);
tmp.searchParams.set("updated", "true");
return NextResponse.redirect(tmp);
}
the api page acted perfectly, but when redirected, the issue throws.(refreshing the redirected page, it shows normally)
@PassionPenguin You can use:
Response.redirect(`${request.nextUrl.origin}/path`, 303);
to solve your issue. I bet you were using redirect() in a POST route handler? If that's the case then you can use the workaround of Response.redirect.
The Next.js team will have to fix redirect() probably. Repository: https://github.com/vvo/next-redirect-connection-closed Live demo: https://next-redirect-connection-closed.vercel.app/ (error codes are 405 on vercel and 500 in dev)
It turns out any <form method="POST">
to a non-existent path will also result in 500 (probably because POST to the error page is the same as POST to a page).
@PassionPenguin You can use:
Response.redirect(`${request.nextUrl.origin}/path`, 303);
to solve your issue. I bet you were using redirect() in a POST route handler? If that's the case then you can use the workaround of Response.redirect.
The Next.js team will have to fix redirect() probably. Repository: https://github.com/vvo/next-redirect-connection-closed Live demo: https://next-redirect-connection-closed.vercel.app/ (error codes are 405 on vercel and 500 in dev)
It turns out any
<form method="POST">
to a non-existent path will also result in 500 (probably because POST to the error page is the same as POST to a page).
yes u're correct! thx!!!!
Verify canary release
Provide environment information
Which area(s) of Next.js are affected? (leave empty if unsure)
Middleware / Edge (API routes, runtime), Standalone mode (output: "standalone")
Link to the code that reproduces this issue or a replay of the bug
npx create-next-app
To Reproduce
npx create-next-app@latest
npx next dev
localhost
and note that in the Response headersconnection: close
is listed. Select other requests and note the same header is sent for all responses.npx next build
to build the app, then runnpx next start
to run itDescribe the Bug
The nodejs server created by Next.js, whether it be in dev mode, production mode after building, or production mode when standalone deployment is configured, returns a
connection: close
response header to clients when rendering routes. This instructs the client to close the underlying TCP connection, rather than returningconnection: keep-alive
which allows the client to reuse the TCP connection when making subsequent requests, greatly reducing latency. This behavior is leading to high latencies for server-rendered Next.js routes.I reproduced this behavior on multiple machines, including OSX and Windows 11, on multiple versions of nodejs, including latest LTS and Current releases, using next 13.4.x and canary, with multiple browsers including Edge and Firefox.
Expected Behavior
The nodejs server created by Next.js should utilize HTTP 1.1 keep-alive to allow clients to keep TCP connections open.
Which browser are you using? (if relevant)
Edge, Firefox
How are you deploying your application? (if relevant)
next start