View Issue Details

This issue affects 1 person(s).
 0
IDProjectCategoryView StatusLast Update
20646Bug reportsQuestion editorpublic2026-08-13 18:12
Reportergrinrus Assigned Totibor.pacalat  
PrioritynoneSeverityblock 
Status closedResolutionduplicate 
Product Version7.0.x 
Summary20646: REST API returns 401 under HTTP/2: case-sensitive Authorization header lookup breaks the new question editor
Description

The REST API rejects valid bearer tokens when the request arrives over HTTP/2, because the Authorization header is looked up with a case-sensitive array key. As a consequence the new question editor never loads: it hangs forever on "Checking permissions...".

ROOT CAUSE

application/libraries/Api/Rest/Endpoint/EndpointFactory.php, getAuthBearerToken() (~line 226 on master):

$headers = getAllHeaders();
if (isset($headers['Authorization'])
    && strpos($headers['Authorization'], 'Bearer ') === 0) {
    $token = substr($headers['Authorization'], 7);
}

The casing of the keys returned by getallheaders() depends on the server stack:

  • nginx + PHP-FPM -> PHP rebuilds the names from $_SERVER and title-cases them, so the key is always "Authorization" and the bug is invisible.
  • Apache + mod_php -> the names are returned exactly as received on the wire.

HTTP/2 (RFC 9113, section 8.2) requires all header field names to be transmitted in lowercase. So on Apache + mod_php + HTTP/2 the key is "authorization", the isset() check fails, no token is extracted, and every authenticated REST call returns 401.

HTTP header field names are case-insensitive by specification (RFC 9110, section 5.1), so a case-sensitive lookup is incorrect regardless of the HTTP version.

WHY IT IS HARD TO DIAGNOSE

The React editor bootstraps by calling GET /rest/v1/user-permissions. PermissionsProvider has no visible failure path for a rejected request, so the UI stays on the "Checking permissions..." spinner indefinitely. There is no error message, nothing in the application log, and the admin session itself is perfectly valid. The practical effect is that the survey editor is completely unusable - surveys cannot be created, edited or activated - while everything else in the admin interface works normally.

EVIDENCE

Same token, same server, only the HTTP version differs (measured with PHP cURL and CURLOPT_HTTP_VERSION):

before fix:   HTTP/2 -> 401     HTTP/1.1 -> 200
after fix:    HTTP/2 -> 200     HTTP/1.1 -> 200

The token itself is valid and correctly stored in lime_sessions. Passing the same token via the ?authToken= query parameter - the documented fallback in getAuthToken() - also returns 200 under HTTP/2, which confirms that only the header lookup is at fault.

Header name as seen by PHP for the very same request:

HTTP/2     getallheaders() -> 'authorization'    isset($headers['Authorization']) === false
HTTP/1.1   getallheaders() -> 'Authorization'    isset($headers['Authorization']) === true

SUGGESTED FIX

Normalise the keys before the lookup - the approach recommended by the PHP manual for getallheaders():

$headers = array_change_key_case(getAllHeaders(), CASE_LOWER);
if (isset($headers['authorization'])
    && strpos($headers['authorization'], 'Bearer ') === 0) {
    $token = substr($headers['authorization'], 7);
}

Verified on our installation: after this change the editor loads and surveys can be created and activated normally over HTTP/2.

A secondary improvement worth considering: PermissionsProvider should surface a failed permissions request instead of showing the spinner forever. The silent failure is what made this take a full day to track down.

ADDITIONAL NOTES

The LimeSurvey 7 Question Editor documentation lists urlFormat => 'path' and URL rewriting as the requirements for the new editor. Both were satisfied here. Neither the Authorization header nor HTTP/2 is mentioned, and there is no troubleshooting entry for the hanging "Checking permissions..." screen.

Note that on many shared hosting providers HTTP/2 is enabled automatically together with HTTPS and cannot be turned off per site, so affected users have no configuration-level workaround and must patch the file.

A similar symptom is reported at https://forum.cloudron.io/topic/15623/new-question-editor-require-config.php-change but with a different root cause (404 on /rest/v1/version-info caused by packaging routing); it was fixed by the packager and never reported upstream.

ON THE REQUEST TO ATTACH AN EXAMPLE SURVEY

No .lss file is attached because the issue does not depend on survey content. The failing call is GET /rest/v1/user-permissions, which the editor makes before any survey is loaded, so it reproduces with any survey - or with none at all, by opening the editor on a freshly created installation.

Steps To Reproduce
  1. Install LimeSurvey 7.x on Apache with mod_php (PHP_SAPI = apache2handler).
  2. Serve it over HTTPS with HTTP/2 enabled.
  3. Log in to the admin interface.
  4. Open any survey in the new question editor, or click "Create survey".

Expected: the editor loads.

Actual: the page stays on "Checking permissions..." forever. Browser devtools show
GET /rest/v1/user-permissions returning 401.

To confirm the cause without patching anything, call the same endpoint with the same
token twice - once over HTTP/2 and once over HTTP/1.1. HTTP/1.1 returns 200,
HTTP/2 returns 401.

TagsNo tags attached.
Bug heat0
Complete LimeSurvey version number (& build)7.0.9+260812
I will donate to the project if issue is resolvedNo
Story point estimate0
BrowserChrome (any; reproducible with curl --http2)
Database type & versionMySQL 8.4.8
Server OS (if known)Ubuntu 22.04
Webserver software & version (if known)Apache 2.4.63 behind nginx 1.21.1 (HTTP/2 terminated by nginx)
PHP Version8.2.28 (SAPI: apache2handler)

Relationships

duplicate of 20623 closedc_schmitz HTTP Authorization header field not treated as case-insensitive leading to new editor not working 

Users monitoring this issue

There are no users monitoring this issue.

Activities

There are no notes attached to this issue.

Issue History

Date Modified Username Field Change
2026-08-11 18:56 grinrus New Issue
2026-08-12 10:26 tibor.pacalat Relationship added duplicate of 20623
2026-08-12 13:03 tibor.pacalat Assigned To => tibor.pacalat
2026-08-12 13:03 tibor.pacalat Status new => resolved
2026-08-12 13:03 tibor.pacalat Resolution open => duplicate
2026-08-13 18:12 c_schmitz Status resolved => closed