passbolt/passbolt_api · warning · ForbiddenException

Healthcheck security index endpoint disabled.

Error message

Healthcheck security index endpoint disabled.

What it means

Passbolt throws this 403 when the healthcheck security index endpoint has been disabled via the config flag PASSBOLT_PLUGINS_HEALTHCHECK_SECURITY_INDEX_ENDPOINT_ENABLED. throwErrorIsEndpointIsDisabled runs in beforeFilter so the request never reaches the action.

Solutions

  1. Set the flag to true in config: Configure::write('passbolt.plugins.healthcheckSecurityIndexEndpoint.enabled', true) or in config/passbolt.php
  2. If the endpoint was disabled intentionally on purpose, update monitoring scripts to use the main /healthcheck.json instead
  3. Check app/config for an environment-specific file disabling the plugin endpoint
  4. Restart/clear cache after config change (cake cache clear_all) so Configure picks it up

Example fix

// before (config/passbolt.php)
'healthcheckSecurityIndexEndpoint' => ['enabled' => false],
// after
'healthcheckSecurityIndexEndpoint' => ['enabled' => true],
Defensive patterns

Strategy: fallback

Validate before calling

const enabled = await serverConfig('passbolt.plugins.healthcheckSecurityIndexEndpoint.enabled');
if (!enabled) return runBasicHealthcheckOnly();

Type guard

null

Try / catch

try {
  return await fetchSecurityIndexHealthcheck();
} catch (e) {
  if (e.status === 403) return fetch('/healthcheck.json').then(r=>r.json()); // fallback to main healthcheck
  throw e;
}

Prevention

When it happens

Trigger: Calling GET /healthcheck/security-index (or the healthcheck UI requesting it) while the flag is set to false in config (e.g. passbolt.plugins.healthcheckSecurityIndexEndpoint.enabled = false).

Common situations: Operators hardening production by disabling detailed healthcheck exposure, then their monitoring scripts still probe the endpoint; config migration dropping the flag default; copy of production config used locally with the endpoint off.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/14ab9fed9464d3aa. Report an issue: GitHub.

Appendix: source

Thrown at src/Controller/Healthcheck/HealthcheckIndexController.php:120

                ->setLayout('login')
                ->setTemplatePath('Healthcheck')
                ->setTemplate('index');
            $this->success(__('All checks ran successfully!'), $body);
        } else {
            $healthcheckResult = $this->formatCollectionResponseAsPerLegacy($resultsGroupByDomain);

            $this->success(__('The operation was successful.'), $healthcheckResult);
        }
    }

    /**
     * @return void
     * @throws \Cake\Http\Exception\ForbiddenException if the endpoint is deactivated
     */
    private function throwErrorIsEndpointIsDisabled(): void
    {
        if (!Configure::read(self::PASSBOLT_PLUGINS_HEALTHCHECK_SECURITY_INDEX_ENDPOINT_ENABLED)) {
            throw new ForbiddenException(__('Healthcheck security index endpoint disabled.'));
        }
    }

    /**
     * @return array
     */
    private function getDomainsIgnore(): array
    {
        return [HealthcheckServiceCollector::DOMAIN_JWT];
    }

    /**
     * Formats given collection as per legacy array structure. This helps us keep backward compatibility.
     *
     * @deprecated As of v4.7.0, this is just to keep backward compatibility.
     * @param \Cake\Collection\CollectionInterface $resultsGroupByDomain Result collection to format as per legacy array format.
     * @return array
     */

View on GitHub (pinned to 31c1bbc10f)