diff --git a/changelog.d/fixed-error-pages-reflect-404.md b/changelog.d/fixed-error-pages-reflect-404.md new file mode 100644 index 0000000000..cdff183046 --- /dev/null +++ b/changelog.d/fixed-error-pages-reflect-404.md @@ -0,0 +1 @@ +- The ingress-nginx error backend (`src/deploy/k8s/error-pages.yaml`) now reflects an intercepted 403 or 404 as that status with a dedicated "Page not found" document (HTML or JSON by caller) instead of falling through to the 503 "Hive is restarting" page with an auto-refresh. With the controller's global `custom-http-errors` list including 404, every missing URL on the hub had been served as a 503, which Google Search Console reported as Soft 404 and which told visitors a healthy hub was down ([#17431](https://github.com/hivecommons/hive/issues/17431)). The global list is documented as `502,503,504`. diff --git a/src/deploy/k8s/error-pages.yaml b/src/deploy/k8s/error-pages.yaml index 79e983ac0f..5922b56473 100644 --- a/src/deploy/k8s/error-pages.yaml +++ b/src/deploy/k8s/error-pages.yaml @@ -60,6 +60,50 @@ data: # "ok": false is the field clients key off (issue #4241). error.json: | {"ok":false,"error":"hive unavailable","detail":"This hive is restarting or its API is temporarily unreachable. The request was NOT applied - retry shortly."} + # Served when the controller intercepts a 403 or 404. These are NOT "retry + # shortly" states: the upstream answered, and the resource is missing or + # forbidden. Before these documents existed, an intercepted 404 fell through + # to the 503 "Hive is restarting" page with an auto-refresh, which Google + # Search Console reported as "Soft 404" on hive.hivecommons.dev and which + # told a human a healthy hub was down (#17431). No spinner, no refresh. + notfound.json: | + {"ok":false,"error":"not found","detail":"No such page or API route on this hive. The request was NOT applied."} + notfound.html: | + + + + + + + Hive — Page Not Found + + + +
+
🔍
+

Page not found

+

The page you're looking for doesn't exist on this hive or has moved.

+

Back to the front page

+
+ + # Served for a 401 intercepted on the hive-api-xhr Ingress (the dashboard's # XHR routes under /api/, see src/pkg/hub/saas_provision.go). That Ingress # deliberately has no auth-signin: fetch() cannot follow the cross-origin @@ -118,7 +162,13 @@ data: # ingress-nginx sends a request here when a hive Ingress intercepts one of # the upstream statuses named in its custom-http-errors annotation ("502,503" # on the hive Ingress, "401,502,503" on the hive-api-xhr Ingress -- see - # src/pkg/hub/saas_provision.go). It rewrites the + # src/pkg/hub/saas_provision.go), or in the controller's GLOBAL + # `custom-http-errors` ConfigMap key for an Ingress with no annotation of + # its own (the hub's). Keep that global list to "502,503,504": with 403 and + # 404 in it, every missing page on hive.hivecommons.dev was intercepted and + # -- because this backend had no 404 branch -- answered as a 503 "Hive is + # restarting" page (#17431). The 403/404 branches below are the safety net + # if the global list ever grows again. It rewrites the # URI to "/" and passes the original request's metadata in headers: # # X-Code the intercepted upstream status, e.g. "502" @@ -165,10 +215,15 @@ data: # 401 gets its own document: it is intercepted from the hive-api-xhr # Ingress (issue #7405) and means "sign in again", not "retry shortly". error_page 401 /unauthorized.$hive_error_ext; + # 403 and 404 share the not-found document: the upstream answered and the + # resource is missing or off-limits, so no spinner and no auto-refresh. + error_page 403 404 /notfound.$hive_error_ext; location = /error.html { internal; } location = /error.json { internal; } location = /unauthorized.html { internal; } location = /unauthorized.json { internal; } + location = /notfound.html { internal; } + location = /notfound.json { internal; } # Probed by the kubelet directly, never through the Ingress, so it is not # affected by the status mapping below. @@ -181,6 +236,8 @@ data: # Reflect the intercepted status back to the caller. `return` answers # every method, so a POST is no longer served a static file with 200. if ($http_x_code = "401") { return 401; } + if ($http_x_code = "403") { return 403; } + if ($http_x_code = "404") { return 404; } if ($http_x_code = "500") { return 500; } if ($http_x_code = "502") { return 502; } if ($http_x_code = "504") { return 504; }