[GH-ISSUE #933] [BUG] Problems with PING_ENDPOINT #656

Closed
opened 2026-02-25 23:43:11 +03:00 by kerem · 4 comments
Owner

Originally created by @hknobbe on GitHub (Dec 22, 2023).
Original GitHub issue: https://github.com/healthchecks/healthchecks/issues/933

Hi!

I was trying to update the PING_ENDPOINT and found some bugs:

When adding de - PING_ENDPOINT to my docker-compose I found some problems:

  1. thedocs show no trailing /, however if you don't add it you get the URL: https://ping-hc.com{uuid} instead of https://ping-hc.com/{uuid}
  2. When you change the PING_ENDPOINT, the URL (https://hc-ping.com/{uuid}) shows a 404. However changing if you change the URL to: https://ping-hc.com/ping/{uuid} you get a OK.

System:

  • Docker behind NGINX proxy
  • :latest

Compose:

version: "3"

networks:
  t2_proxy:
    name: t2_proxy
    external: true    
  Havvxxxxxxxx:
    name: Havvxxxxxxxx
    external: true
    
services:
  healthchecks:
    user: "1000:1000"
    image: healthchecks/healthchecks:latest
    networks:
      t2_proxy:
        ipv4_address: 172.18.0.40
      Havvxxxxxxxx:
        ipv4_address: 10.0.0.27
    container_name: healthchecks
    environment:
      - DB=postgres
      - DB_HOST=*******************
      - DB_PORT=5432
      - DB_NAME=*******************
      - DB_USER=*******************
      - DB_PASSWORD=*******************
      - TZ=Europe/Amsterdam
      - SITE_ROOT=https://hc.*******************.network
      - SITE_NAME= Healthchecks
      - DEFAULT_FROM_EMAIL=healthchecks@*******************.network
      - EMAIL_HOST=mail.*******************.network
      - EMAIL_PORT=587
      - EMAIL_HOST_USER=healthchecks@*******************.network
      - EMAIL_HOST_PASSWORD=*******************
      - EMAIL_USE_TLS=True
      - PING_EMAIL_DOMAIN=hc.*******************.network
      - SUPERUSER_EMAIL=**********************
      - SUPERUSER_PASSWORD=********************
      - REGENERATE_SETTINGS= #optional
      - ALLOWED_HOSTS=hc.*******************.network,ping.hc.*******************.network
      - APPRISE_ENABLED= #optional
      - DEBUG=False
      - INTEGRATIONS_ALLOW_PRIVATE_IPS= #optional
      - PING_ENDPOINT=https://ping.hc.*******************.network/
      - SECRET_KEY=************************************
      - SITE_LOGO_URL=https://auth.**********************.nl/media/application-icons/1_m_ad1TBlJEKTn9n2sKZqgg.png
      - REMOTE_USER_HEADER=HTTP_X_XXXXXXXXXXXXXXXXXXXXX
      - REGISTRATION_OPEN=False
      - USE_X_FORWARDED_HOST=True
    volumes:
      - /home/************/docker/healthcheck/data:/data
      - /home/************/docker/healthcheck/docker/uwsgi.ini:/opt/healthchecks/docker/uwsgi.ini
      - /home/************/docker/healthcheck/config/settings.py:/opt/healthchecks/hc/settings.py:rw
    restart: unless-stopped 

No strange things in the logs.

Everything else works as it suppose to.

Let me know if you need anything else.

Originally created by @hknobbe on GitHub (Dec 22, 2023). Original GitHub issue: https://github.com/healthchecks/healthchecks/issues/933 Hi! I was trying to update the PING_ENDPOINT and found some bugs: When adding de - PING_ENDPOINT to my docker-compose I found some problems: 1. thedocs show no trailing /, however if you don't add it you get the URL: https://ping-hc.com{uuid} instead of https://ping-hc.com/{uuid} 2. When you change the PING_ENDPOINT, the URL (https://hc-ping.com/{uuid}) shows a 404. However changing if you change the URL to: https://ping-hc.com/ping/{uuid} you get a OK. System: - Docker behind NGINX proxy - :latest Compose: ``` version: "3" networks: t2_proxy: name: t2_proxy external: true Havvxxxxxxxx: name: Havvxxxxxxxx external: true services: healthchecks: user: "1000:1000" image: healthchecks/healthchecks:latest networks: t2_proxy: ipv4_address: 172.18.0.40 Havvxxxxxxxx: ipv4_address: 10.0.0.27 container_name: healthchecks environment: - DB=postgres - DB_HOST=******************* - DB_PORT=5432 - DB_NAME=******************* - DB_USER=******************* - DB_PASSWORD=******************* - TZ=Europe/Amsterdam - SITE_ROOT=https://hc.*******************.network - SITE_NAME= Healthchecks - DEFAULT_FROM_EMAIL=healthchecks@*******************.network - EMAIL_HOST=mail.*******************.network - EMAIL_PORT=587 - EMAIL_HOST_USER=healthchecks@*******************.network - EMAIL_HOST_PASSWORD=******************* - EMAIL_USE_TLS=True - PING_EMAIL_DOMAIN=hc.*******************.network - SUPERUSER_EMAIL=********************** - SUPERUSER_PASSWORD=******************** - REGENERATE_SETTINGS= #optional - ALLOWED_HOSTS=hc.*******************.network,ping.hc.*******************.network - APPRISE_ENABLED= #optional - DEBUG=False - INTEGRATIONS_ALLOW_PRIVATE_IPS= #optional - PING_ENDPOINT=https://ping.hc.*******************.network/ - SECRET_KEY=************************************ - SITE_LOGO_URL=https://auth.**********************.nl/media/application-icons/1_m_ad1TBlJEKTn9n2sKZqgg.png - REMOTE_USER_HEADER=HTTP_X_XXXXXXXXXXXXXXXXXXXXX - REGISTRATION_OPEN=False - USE_X_FORWARDED_HOST=True volumes: - /home/************/docker/healthcheck/data:/data - /home/************/docker/healthcheck/docker/uwsgi.ini:/opt/healthchecks/docker/uwsgi.ini - /home/************/docker/healthcheck/config/settings.py:/opt/healthchecks/hc/settings.py:rw restart: unless-stopped ``` No strange things in the logs. Everything else works as it suppose to. Let me know if you need anything else.
kerem closed this issue 2026-02-25 23:43:11 +03:00
Author
Owner

@hknobbe commented on GitHub (Dec 22, 2023):

Update:

The UI shows:

How To Ping

Keep this check up by making HTTP requests to this URL:
https://ping.hc.s***********************.network/c803b96c-xxxx-xxxx-xxxx-3cd70b0ca02c
<!-- gh-comment-id:1867384566 --> @hknobbe commented on GitHub (Dec 22, 2023): Update: The UI shows: ``` How To Ping Keep this check up by making HTTP requests to this URL: https://ping.hc.s***********************.network/c803b96c-xxxx-xxxx-xxxx-3cd70b0ca02c ```
Author
Owner

@cuu508 commented on GitHub (Dec 22, 2023):

Thanks for the report. I added the following notes to PING_ENDPOINT docs:

  • Make sure the PING_ENDPOINT value ends with a trailing slash. If a trailing slash
    is missing, Healthchecks will not add it implicitly.
  • Healthchecks uses PING_ENDPOINT for formatting ping URLs for display.
    The PING_ENDPOINT value does not influence the routing of incoming HTTP requests.
    If you change the PING_ENDPOINT value, you will likely also need to add matching
    URL rewriting rules in your reverse proxy configuration.
<!-- gh-comment-id:1867611377 --> @cuu508 commented on GitHub (Dec 22, 2023): Thanks for the report. I added the following notes to PING_ENDPOINT docs: > * Make sure the `PING_ENDPOINT` value ends with a trailing slash. If a trailing slash > is missing, Healthchecks will *not* add it implicitly. > * Healthchecks uses `PING_ENDPOINT` for formatting ping URLs for display. > The `PING_ENDPOINT` value does not influence the routing of incoming HTTP requests. > If you change the `PING_ENDPOINT` value, you will likely also need to add matching > URL rewriting rules in your reverse proxy configuration.
Author
Owner

@hknobbe commented on GitHub (Dec 22, 2023):

awesome @cuu508 thanks!

For the archives, and anyone who is running in to the same issue.
I used this for NGINX Reverse Proxy:

location ~* ^/(.*) {
    proxy_pass          $forward_scheme://$server:$port/ping/$1$is_args$args;
    # All your other stuf goes here.
}
<!-- gh-comment-id:1867639460 --> @hknobbe commented on GitHub (Dec 22, 2023): awesome @cuu508 thanks! For the archives, and anyone who is running in to the same issue. I used this for NGINX Reverse Proxy: ``` location ~* ^/(.*) { proxy_pass $forward_scheme://$server:$port/ping/$1$is_args$args; # All your other stuf goes here. } ```
Author
Owner

@purplebite commented on GitHub (Aug 4, 2025):

For the archives and anyone else encountering this issue, I've successfully solved the PING_ENDPOINT problem with a custom Django middleware solution. This approach eliminates the need for a reverse proxy and enables direct deployment to platforms like Fly.io.

The Problem

When using a separate PING_ENDPOINT domain (e.g., https://ping.hc.example.com/), requests to that domain return 404 errors because Django doesn't know how to route them to the /ping/ endpoints.

My Solution

I created a custom middleware that automatically rewrites requests from the ping domain to include the /ping/ prefix. Here's how it works and ignore the single Fly.io specific line to copy fly.toml into the docker to ensure environment variables are available.

Dockerfile:

FROM healthchecks/healthchecks:latest

# Copy fly.toml to ensure environment variables are available
COPY fly.toml /fly.toml

# Custom Django settings for domain routing
COPY local_settings.py /opt/healthchecks/hc/local_settings.py

# Switch to root to modify files
USER root

# Change port 8000 to 8080 in uwsgi.ini
RUN sed -i 's/:8000/:8080/' /opt/healthchecks/docker/uwsgi.ini

# Verify the change was made
RUN grep -q ":8080" /opt/healthchecks/docker/uwsgi.ini >/dev/null || (echo "Port change failed!" >&2 && exit 1)

# Switch back to the hc user
USER hc

local_settings.py (Custom Middleware):

# Optional ping domain routing middleware
import os
import logging
from urllib.parse import urlparse

class PingDomainRoutingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.ping_domain = None
        
        # Extract domains from PING_ENDPOINT and SITE_ROOT
        ping_endpoint = os.environ.get('PING_ENDPOINT', '')
        site_root = os.environ.get('SITE_ROOT', '')
        
        if ping_endpoint and site_root:
            # Extract just the domain part (ignore paths, trailing slashes, etc.)
            ping_domain = urlparse(ping_endpoint).netloc.lower()
            site_domain = urlparse(site_root).netloc.lower()
            
            # Only enable rewriting if domains are actually different
            if ping_domain != site_domain:
                self.ping_domain = ping_domain
                print(f"Ping domain routing enabled: {ping_domain} → /ping/")

    def __call__(self, request):
        # Only rewrite if we have different domains configured
        if not self.ping_domain:
            return self.get_response(request)
            
        host = request.get_host().lower()
        
        # If request is for ping domain, rewrite if not already starts with /ping/
        if host == self.ping_domain:
            if not request.path.startswith('/ping/'):
                original_path = request.path
                request.path_info = '/ping' + request.path_info
                request.path = '/ping' + request.path
                
                # Optional: Log the rewrite when DEBUG is enabled
                if os.environ.get('DEBUG', 'false').lower() == 'true':
                    print(f"Ping domain rewrite: {host}{original_path}{host}{request.path}")
                
        response = self.get_response(request)
        return response

# Import the existing MIDDLEWARE setting
from hc.settings import MIDDLEWARE

# Add our ping domain routing middleware at the beginning
MIDDLEWARE = [
    'hc.local_settings.PingDomainRoutingMiddleware',
] + MIDDLEWARE

How It Works

  1. The middleware extracts the ping domain from PING_ENDPOINT and compares it to SITE_ROOT
  2. If they're different domains, it enables automatic path rewriting
  3. When a request comes to the ping domain, it automatically prefixes the path with /ping/
  4. This makes https://ping.hc.example.com/{uuid} work as expected by internally routing to /ping/{uuid}

@cuu508 - I'm wondering if this functionality (or something similar) should be part of the core application? It seems counterintuitive that you can configure a PING_ENDPOINT but then need to handle the routing yourself outside the application. The middleware approach works well for my usage, but having built-in support for separate ping domains would make the feature more user-friendly out of the box.

<!-- gh-comment-id:3150201033 --> @purplebite commented on GitHub (Aug 4, 2025): For the archives and anyone else encountering this issue, I've successfully solved the `PING_ENDPOINT` problem with a custom Django middleware solution. This approach eliminates the need for a reverse proxy and enables direct deployment to platforms like Fly.io. ### The Problem When using a separate `PING_ENDPOINT` domain (e.g., `https://ping.hc.example.com/`), requests to that domain return 404 errors because Django doesn't know how to route them to the `/ping/` endpoints. ### My Solution I created a custom middleware that automatically rewrites requests from the ping domain to include the `/ping/` prefix. Here's how it works and ignore the single Fly.io specific line to copy fly.toml into the docker to ensure environment variables are available. **Dockerfile:** ```dockerfile FROM healthchecks/healthchecks:latest # Copy fly.toml to ensure environment variables are available COPY fly.toml /fly.toml # Custom Django settings for domain routing COPY local_settings.py /opt/healthchecks/hc/local_settings.py # Switch to root to modify files USER root # Change port 8000 to 8080 in uwsgi.ini RUN sed -i 's/:8000/:8080/' /opt/healthchecks/docker/uwsgi.ini # Verify the change was made RUN grep -q ":8080" /opt/healthchecks/docker/uwsgi.ini >/dev/null || (echo "Port change failed!" >&2 && exit 1) # Switch back to the hc user USER hc ``` **local_settings.py (Custom Middleware):** ```python # Optional ping domain routing middleware import os import logging from urllib.parse import urlparse class PingDomainRoutingMiddleware: def __init__(self, get_response): self.get_response = get_response self.ping_domain = None # Extract domains from PING_ENDPOINT and SITE_ROOT ping_endpoint = os.environ.get('PING_ENDPOINT', '') site_root = os.environ.get('SITE_ROOT', '') if ping_endpoint and site_root: # Extract just the domain part (ignore paths, trailing slashes, etc.) ping_domain = urlparse(ping_endpoint).netloc.lower() site_domain = urlparse(site_root).netloc.lower() # Only enable rewriting if domains are actually different if ping_domain != site_domain: self.ping_domain = ping_domain print(f"Ping domain routing enabled: {ping_domain} → /ping/") def __call__(self, request): # Only rewrite if we have different domains configured if not self.ping_domain: return self.get_response(request) host = request.get_host().lower() # If request is for ping domain, rewrite if not already starts with /ping/ if host == self.ping_domain: if not request.path.startswith('/ping/'): original_path = request.path request.path_info = '/ping' + request.path_info request.path = '/ping' + request.path # Optional: Log the rewrite when DEBUG is enabled if os.environ.get('DEBUG', 'false').lower() == 'true': print(f"Ping domain rewrite: {host}{original_path} → {host}{request.path}") response = self.get_response(request) return response # Import the existing MIDDLEWARE setting from hc.settings import MIDDLEWARE # Add our ping domain routing middleware at the beginning MIDDLEWARE = [ 'hc.local_settings.PingDomainRoutingMiddleware', ] + MIDDLEWARE ``` ### How It Works 1. The middleware extracts the ping domain from `PING_ENDPOINT` and compares it to `SITE_ROOT` 2. If they're different domains, it enables automatic path rewriting 3. When a request comes to the ping domain, it automatically prefixes the path with `/ping/` 4. This makes `https://ping.hc.example.com/{uuid}` work as expected by internally routing to `/ping/{uuid}` **@cuu508** - I'm wondering if this functionality (or something similar) should be part of the core application? It seems counterintuitive that you can configure a `PING_ENDPOINT` but then need to handle the routing yourself outside the application. The middleware approach works well for my usage, but having built-in support for separate ping domains would make the feature more user-friendly out of the box.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/healthchecks#656
No description provided.