I am designing and developing Healthchecks with the primary goal of running the hosted service at https://healthchecks.io. But I am also happy to incorporate features that enable or simplify self-hosting use cases. Examples include the first-party Docker image, the remote authentication support, the Apprise integration, the Shell commands integration. A more niche feature that has come up a few times is the ability to serve Healthchecks on a subpath. Typically Healthchecks would run on a root level of a domain:
https://hc.example.org
But in some scenarios, you may want to have multiple apps on the same domain, each under its own path:
https://example.org/hc
When this feature request first came up, I fixed various bits of the codebase to get the basics working. The main areas of work were:
- HTML templates: add path prefix to URLs, do not use hardcoded URLs.
- JavaScript: add path prefix to URLs in AJAX calls.
- Python: make sure the URLs generated with
django.urls.reverse()
include the path prefix.
Python code can figure out the path prefix from request headers in some cases but not all. The manage.py sendalerts
and manage.py sendreports
commands run outside the request-response cycle, and HTTP request data is unavailable. My initial solution for determining the path prefix in management commands was to rely on the FORCE_SCRIPT_NAME Django setting.
With the initial fixes in place, it was technically possible to run Healthchecks on a subpath, but Healthchecks had no documentation besides the GitHub issue comments. Setting it up on any particular reverse proxy (Apache, nginx, Caddy, …) and WSGI server (uWSGI, Gunicorn, uWSGI inside Docker container) combination required some trial and error. WSGI has a standard mechanism for running an app under a path prefix – the SCRIPT_NAME variable, but each reverse proxy and each WSGI server has its unique configuration settings and limitations for it. Documenting and maintaining various configurations would be a nightmare task. And, remember, my primary goal is running Healthchecks.io. The self-hosted instances are a side objective.
I received a good tip from Florian Apolloner to ditch the reliance on SCRIPT_NAME
and FORCE_SCRIPT_NAME
, and instead inject the path prefix in Django URL patterns. I followed the advice and, starting from Healthchecks v3.8, you can serve Healthchecks on a subpath by adding a path prefix to the SITE_ROOT configuration parameter:
# This is how you usually set SITE_ROOT:
SITE_ROOT=https://example.org
# Bit since v3.8 this also works:
SITE_ROOT=https://example.org/hc
The reverse proxy can proxy URLs with a certain prefix to the WSGI server without extra processing (such as rewriting the URL or setting SCRIPT_NAME). As an example, here is a configuration fragment for nginx that could go inside a “server” block:
location /hc/ {
include proxy_params;
proxy_pass http://localhost:8000;
}
And an example Caddyfile:
example.org {
handle /hc/* {
reverse_proxy localhost:8000
}
}
Serving Healthchecks at the root of the domain is the preferred way. But, if you need to serve Healthchecks under a path prefix, you can! Happy self-hosting,
–Pēteris