fix: stop caching a failed /api/config - #839
Merged
Merged
Conversation
app.js stamps max-age=60 on every unauthenticated GET before routing, so it lands on this route's 404 and 500 too. Once the nginx exact-match block is removed and eagle-api actually answers the path, a few seconds of Mongo trouble would be cached at rproxy and in every browser for a full minute after the database recovers. A good config stays cacheable; a failure does not.
danieltruong
requested review from
Ckoelewyn,
tolkamps1 and
tom0827
as code owners
August 14, 2026 17:34
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
app.js:75-80stampsCache-Control: max-age=60on every unauthenticated GET before routing, so it lands on/api/config's 404 and 500 as well as its 200. The controller set no header of its own. Both error paths now setno-store; the 200 stays cacheable.Why this matters, and why now
Today this is theoretical:
eao-nginx'slocation = /api/configis an exact match and beats the/apiproxy, so this controller is unreachable and nothing here changes behaviour on deploy.It stops being theoretical at cutover. Once eagle-api answers the path, a three-second Mongo blip returns one 500 — and that 500 is then cached at rproxy and in every browser for a full minute after the database has recovered. The cache converts a transient into a minute-long outage for anyone who loads a page during it.
This is a precondition for the frontend work that reacts to a failed config fetch (bcgov/eagle-admin#921), which is why it lands ahead of the cutover rather than with it.
Mechanism
Actions.sendResponse(api/helpers/actions.js:59-61) isres.status().json()and overrides no headers, so ares.setHeaderimmediately before it wins over the app-level default.Not in this PR
The same pre-routing stamp still marks every other public 404 and 500 cacheable for a minute. The real fix is to downgrade any status ≥ 400 to
no-store— anon-headershook or ares.writeHeadwrapper — which changes behaviour on every public route and wants its own ticket and its own soak. Raised in review; deliberately not folded in here.Also still to come, in the
eao-nginxcutover commit rather than this one:proxy_cache_valid 200 60sandproxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504on the/apiblock. The repo currently hasproxy_cache globalcachewith neither directive anywhere, so a good config does not keep serving through an eagle-api blip.Tests
Two cases added to
test/controllers/config.test.jsassertingCache-Control: no-storeon the 404 and the 500. The existingfakeRes()had nosetHeader, so it gained one — without it the pre-existing 404 and 500 tests would have thrown.Full suite: 638 passing.