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
- Set the flag to true in config: Configure::write('passbolt.plugins.healthcheckSecurityIndexEndpoint.enabled', true) or in config/passbolt.php
- If the endpoint was disabled intentionally on purpose, update monitoring scripts to use the main /healthcheck.json instead
- Check app/config for an environment-specific file disabling the plugin endpoint
- 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
- Detect the flag in server config before probing security-index endpoint
- Fall back to the aggregate /healthcheck.json endpoint
- Coordinate monitoring scripts with ops hardening decisions
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
- Password expiry is not activated.
- V5 metadata format is not enabled.
- Access restricted to administrators.
- Additional resource types are not enabled on this server.
- Could not generate TOTP secret, please try again later.
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)