Description
Description
setcookie() currently just adds another HTTP header (= like calling header( 'Set-Cookie: ...', false );
in all cases.
This means if the same cookie (= functionaly identical, e.g. same name+path+domain+...) is set multiple times (e.g. different value/expiration or also for whatever reason same value/expiration) it will just add another Set-Cookie header.
Since HTTP headers cannot be compressed* or compression happens upstream, this causes significant performance downsides:
-
upstream needs to allocate larger buffers (nginx
fastcgi_buffer
) that exceed 1 or 2 memory pages for the odd request that has this issue, leading to significantly higher memory consumption just for the rare request that has this issue. e.g. only 0.0001% of requests send headers that exceed 4k (one memory page). Those requests send headers with an average size of 12k and a maximum of 16k.
This means that a buffer size of 16k has to be allocated, which means that if we can serve up to 100000 requests simultaneously (= 2000 pages that each contain ~50 js/css/images) that's more than 1GB of memory unnecessarily allocated/wasted just to ensure that the 1 request that has this issue to not causeupstream sent too big header while reading response header from upstream
-
the overall page load can take significantly longer - e.g. HTTP headers are 6k and compressed page is 6k (assuming the page is served from a PHP page cache to ignore any other effects). If the set-cookie headers are deduplicated it's HTTP headers 0.5k + 6k effectively reducing the overall page load time by almost 50% in this example:
Date: Fri, 28 Mar 2025 08:44:58 GMT
Content-Type: application/json; charset=UTF-8
X-Content-Type-Options: nosniff
Access-Control-Expose-Headers:
Access-Control-Allow-Headers:
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=at; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=be; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=bn; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=cf; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=ch; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=da; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=de; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=en; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=es; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=fi; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=fr; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=ie; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=it; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=pt; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=sv; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Set-Cookie: some_test_foo_bar_cookie_d41d8cd98f00b204e9870998ecf8421e=nl; expires=Sat, 29-Mar-2025 08:44:59 GMT; Max-Age=86400; path=/; secure
Allow: GET, POST, PUT, PATCH, DELETE
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0, no-store, private
X-Accel-Buffering: no
Content-Encoding: br
Vary: Accept-Encoding
While it's true that in many cases, these duplicate setcookie calls indicate an issue in the application, it's often outside of the scope (= 3rd party dependencies causing it or people not being aware of this issue at all in the first place) of the application.
This change could result in a significant performance gain (= time until the first part of the document is loaded) for all PHP applications on pages where they set cookies.
*in http2/3 they can, but it's practically not supported or implemented anywhere and that's unlikely to change