You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Login with "Remember me" returns 502 behind nginx: response headers exceed default proxy_buffer_size (4 KB)
#4901
v7.11.1 (also reproduced on current master, e9bd4992d)
Did you check the latest Lychee version?
Yes, I did
Which PHP version are you using?
PHP 8.4
Detailed description of the problem
Behind an nginx reverse proxy with default buffer settings, logging in with "Remember me" checked fails with a 502 Bad Gateway. Logging in without "Remember me" works. nginx logs:
[error] upstream sent too big header while reading response header from upstream, ... request: "POST /api/v2/Auth::login HTTP/1.1", upstream: "http://<lychee-host>/api/v2/Auth::login"
nginx reads the upstream status line and headers into a single buffer, proxy_buffer_size. Its default is one memory page, which is 4 KB on x86_64 Linux. If the headers don't fit, nginx returns 502.
I measured the response headers of POST /api/v2/Auth::login from a feature test on master (wire format, so Name: value\r\n):
Request
Response header size
remember_me: false
~3,420 bytes
remember_me: true
~4,060 bytes
remember_me: true adds a third cookie (remember_lychee_<sha1>=…, ~630 bytes). In a test environment the response is then only ~35 bytes below 4 KB. A real deployment goes over: ; secure on each cookie, HSTS when SECURITY_HEADER_HSTS_ENABLE is set, any extra SECURITY_HEADER_CSP_* sources, and the container's web server headers.
The remember cookie is what tips it over, but most of the space goes to two headers that Bepsvpt\SecureHeaders\SecureHeadersMiddleware (registered globally in app/Http/Kernel.php) adds to every response, API calls included:
Header
Size
Content-Security-Policy
~1,200 bytes
Permissions-Policy
~790 bytes
That is ~2 KB, half of nginx's default buffer, on every API response. Both headers only take effect on responses rendered as documents (and on workers). Browsers ignore them on JSON and 204 responses returned to fetch/XHR, which is all the api middleware group serves. The other exceptions are the zip download, which is sent as an attachment, and the embed stream, which is JSON.
Lychee is the only application behind my proxy that runs into this. Most apps don't send a large CSP and a fully enumerated Permissions-Policy on API responses.
Workaround for anyone who hits this:
proxy_buffer_size16k;proxy_buffers 8 16k;
It would be better if Lychee worked with a stock nginx config, though. Possible fixes, smallest and least controversial first:
Drop Permissions-Policy from api group responses. It has no effect on non-document responses. This saves ~790 bytes and brings a remember-me login to ~3.3 KB. The change is a small middleware in the api group that removes the header after SecureHeadersMiddleware has run. HTML responses are unchanged.
Also drop or shrink Content-Security-Policy on api group responses. This saves another ~1.2 KB, bringing a remember-me login to ~2.1 KB. That leaves room for users who extend the CSP through SECURITY_HEADER_CSP_* and for future cookies such as WebAuthn or OAuth. If a CSP should stay on API responses as defence in depth, it could be reduced to default-src 'none' plus the configured frame-ancestors, so SECURITY_HEADER_CSP_FRAME_ANCESTORS / iframe embedding keeps working. X-Frame-Options, X-Content-Type-Options and HSTS would stay as they are.
Trim Permissions-Policy in config/secure-headers.php for all responses. It lists dozens of features as (self), and for most of them that's already the browser default. Listing only the features Lychee actually restricts or widens would shrink HTML responses too. This is independent of 1 and 2.
Document the nginx setting in the reverse-proxy docs. This doesn't fix anything by itself, but it would help users on older versions.
Making the remember cookie itself smaller isn't realistic. Its size comes from Laravel's encrypted recaller value and the guard-name hash in the cookie name.
I'm happy to send a PR for whichever option you prefer. My suggestion is 1, optionally with 2.
Steps to reproduce the issue
Put Lychee behind nginx with the default proxy_buffer_size (no buffer tuning), e.g. location / { proxy_pass http://lychee:80; }, on an x86_64 host.
Open the login dialog, enter valid credentials, and check Remember me.
Submit. POST /api/v2/Auth::login returns 502, and the nginx error log shows upstream sent too big header.
Log in again without Remember me. It succeeds.
To measure the headers without nginx, dump the response headers of a remember-me login in a test:
This doesn't depend on a particular install: the header sizes above come from the test suite on master with default configuration. I can provide diagnostics from my instance if they would help.
Browser & System
Any browser. Upstream Lychee behind nginx as a reverse proxy on x86_64 Linux, with nginx's default proxy_buffer_size (4k).
Please confirm
I have provided easy and step-by-step instructions to reproduce the bug.
I understand my bug report will be removed if I haven't met the criteria above.
I understand that if I am requested to provide more information, I must do so within 14 days or the bug report will be closed.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Lychee version
v7.11.1 (also reproduced on current
master,e9bd4992d)Did you check the latest Lychee version?
Yes, I did
Which PHP version are you using?
PHP 8.4
Detailed description of the problem
Behind an nginx reverse proxy with default buffer settings, logging in with "Remember me" checked fails with a 502 Bad Gateway. Logging in without "Remember me" works. nginx logs:
nginx reads the upstream status line and headers into a single buffer,
proxy_buffer_size. Its default is one memory page, which is 4 KB on x86_64 Linux. If the headers don't fit, nginx returns 502.I measured the response headers of
POST /api/v2/Auth::loginfrom a feature test onmaster(wire format, soName: value\r\n):remember_me: falseremember_me: trueremember_me: trueadds a third cookie (remember_lychee_<sha1>=…, ~630 bytes). In a test environment the response is then only ~35 bytes below 4 KB. A real deployment goes over:; secureon each cookie, HSTS whenSECURITY_HEADER_HSTS_ENABLEis set, any extraSECURITY_HEADER_CSP_*sources, and the container's web server headers.The remember cookie is what tips it over, but most of the space goes to two headers that
Bepsvpt\SecureHeaders\SecureHeadersMiddleware(registered globally inapp/Http/Kernel.php) adds to every response, API calls included:Content-Security-PolicyPermissions-PolicyThat is ~2 KB, half of nginx's default buffer, on every API response. Both headers only take effect on responses rendered as documents (and on workers). Browsers ignore them on JSON and 204 responses returned to
fetch/XHR, which is all theapimiddleware group serves. The other exceptions are the zip download, which is sent as an attachment, and the embed stream, which is JSON.Lychee is the only application behind my proxy that runs into this. Most apps don't send a large CSP and a fully enumerated Permissions-Policy on API responses.
Workaround for anyone who hits this:
It would be better if Lychee worked with a stock nginx config, though. Possible fixes, smallest and least controversial first:
Permissions-Policyfromapigroup responses. It has no effect on non-document responses. This saves ~790 bytes and brings a remember-me login to ~3.3 KB. The change is a small middleware in theapigroup that removes the header afterSecureHeadersMiddlewarehas run. HTML responses are unchanged.Content-Security-Policyonapigroup responses. This saves another ~1.2 KB, bringing a remember-me login to ~2.1 KB. That leaves room for users who extend the CSP throughSECURITY_HEADER_CSP_*and for future cookies such as WebAuthn or OAuth. If a CSP should stay on API responses as defence in depth, it could be reduced todefault-src 'none'plus the configuredframe-ancestors, soSECURITY_HEADER_CSP_FRAME_ANCESTORS/ iframe embedding keeps working.X-Frame-Options,X-Content-Type-Optionsand HSTS would stay as they are.Permissions-Policyinconfig/secure-headers.phpfor all responses. It lists dozens of features as(self), and for most of them that's already the browser default. Listing only the features Lychee actually restricts or widens would shrink HTML responses too. This is independent of 1 and 2.Making the remember cookie itself smaller isn't realistic. Its size comes from Laravel's encrypted recaller value and the guard-name hash in the cookie name.
I'm happy to send a PR for whichever option you prefer. My suggestion is 1, optionally with 2.
Steps to reproduce the issue
proxy_buffer_size(no buffer tuning), e.g.location / { proxy_pass http://lychee:80; }, on an x86_64 host.POST /api/v2/Auth::loginreturns 502, and the nginx error log showsupstream sent too big header.To measure the headers without nginx, dump the response headers of a remember-me login in a test:
Diagnostics
This doesn't depend on a particular install: the header sizes above come from the test suite on
masterwith default configuration. I can provide diagnostics from my instance if they would help.Browser & System
Any browser. Upstream Lychee behind nginx as a reverse proxy on x86_64 Linux, with nginx's default
proxy_buffer_size(4k).Please confirm
All reactions