Recover Bearer Auth Behind Proxies That Strip Authorization
Some shared-hosting front proxies remove the standard Authorization header before the request ever reaches PHP (observed in production on HETEML-class hosting). Custom headers pass through, Authorization does not — so the usual recovery tricks fail too:
.htaccessRewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]— useless, Apache never sees the header;CGIPassAuth on— same reason.
The result is that every Bearer-protected endpoint answers 401 missing_token even though the browser sent a perfectly valid token.
NENE2 ships a standard two-part fix (see ADR 0019):
- Frontend:
@hideyukimori/nene2-client(≥ 1.1.0) mirrors the token intoX-Authorization: Bearer <token>on every request, alongside the standard header. - Backend:
Nene2\Middleware\AuthorizationHeaderFallbackMiddlewareadopts the mirror only whenAuthorizationis absent or empty. Hosts that deliver the standard header are byte-for-byte unaffected.
Enable it in the default pipeline
One opt-in flag on RuntimeApplicationFactory:
php
$app = (new RuntimeApplicationFactory(
$psr17, $psr17,
routeRegistrars: [/* ... */],
authMiddleware: $bearerMiddleware,
enableAuthorizationHeaderFallback: true, // off by default
))->create();1
2
3
4
5
6
2
3
4
5
6
When enabled, the fallback runs at the start of the auth stage — before the machine API-key check and before any injected auth middleware — so every credential-reading middleware sees the restored header. It is method- and path-independent.
When the frontend and the API live on different origins, the browser preflight must also accept the mirror header. Enabling the flag takes care of that: X-Authorization is appended to the built-in CORS Access-Control-Allow-Headers allowlist automatically, so the allowedOrigins you already pass to the factory is all the CORS setup you need. To allow further custom request headers, pass the full list via corsAllowedHeaders.
Or wire it manually
In a hand-assembled pipeline, place it anywhere before your auth middleware:
php
$stack = [
// ... request id, logging, security headers, CORS, error handling ...
new AuthorizationHeaderFallbackMiddleware(),
$bearerMiddleware,
];1
2
3
4
5
2
3
4
5
Outside a PSR-15 pipeline, the transform is available as a static helper:
php
$request = AuthorizationHeaderFallbackMiddleware::apply($request);1
When you must NOT enable it
Enabling the fallback makes X-Authorization credential-equivalent to Authorization. That is exactly right on hosts that strip the header accidentally — and exactly wrong where an upstream strips it deliberately:
- a gateway that performs authentication itself and forwards a trusted identity;
- a WAF that filters inbound credentials from untrusted clients.
In those setups the mirror would be a client-controlled bypass. Either keep the flag off, or make the upstream strip X-Authorization too.
Also treat X-Authorization with the same confidentiality as Authorization in access logs and intermediate proxies.
Notes
- The header name is fixed (
AuthorizationHeaderFallbackMiddleware::FALLBACK_HEADER,X-Authorization). It is a fleet-wide wiring contract with the frontend client, not a tuning knob. - The mirror value is adopted verbatim (
Bearer <token>included). Token validation stays entirely in your auth middleware — a garbage mirror fails exactly like a garbage standard header. - Precedence is always: non-empty
Authorizationwins; the mirror is only consulted when the standard header is absent or empty.