Serveur interne

OxPHP exécute un serveur HTTP distinct sur un port dédié pour les contrôles de santé, les métriques Prometheus et l'inspection de la configuration en direct. Il reste totalement isolé de l'écouteur de l'application principale : pas de TLS, pas de limitation de débit, pas d'ID de requête et aucun traitement d'événements.

Fonctionnement

Définissez INTERNAL_ADDR avec une adresse d'écoute (par exemple 127.0.0.1:9090) et OxPHP démarre un second écouteur HTTP sur cette adresse. Le serveur interne expose les endpoints intégrés ci-dessous :

  • /health — statut JSON agrégé
  • /health/liveness, /healthz (alias) — sonde de vivacité
  • /health/readiness, /readyz (alias) — sonde de disponibilité
  • /health/startup, /startupz (alias) — sonde de démarrage
  • /metrics — exposition Prometheus
  • /config — configuration active du serveur au format JSON

Les plugins peuvent enregistrer des endpoints supplémentaires sous le préfixe /__<plugin>/. Pendant l'arrêt gracieux, le serveur interne reste disponible jusqu'à ce que le serveur principal ait terminé de vider ses connexions.

Note

Le serveur interne ne démarre que lorsque INTERNAL_ADDR est explicitement défini. Sans cette variable, les endpoints de santé, de métriques et de configuration ne sont pas accessibles via HTTP.

Configuration

Variable Valeur par défaut Description
INTERNAL_ADDR (non défini) Adresse du serveur interne. Non démarré lorsqu'il n'est pas défini. Une valeur ne comportant qu'un port (:9090 ou 9090) se lie à 127.0.0.1 ; utilisez explicitement 0.0.0.0:9090 pour l'exposer hors de l'hôte. Exemple : 127.0.0.1:9090
INTERNAL_ALLOW_IPS (non défini) Liste d'autorisation CIDR/IP séparée par des virgules. Les pairs en dehors de la liste reçoivent 403 sur les chemins /metrics, /config et /__<plugin>/ ; les sondes de santé restent toujours accessibles. Non défini/vide = tout autoriser. Le loopback n'est pas implicite — indiquez 127.0.0.1/32 pour conserver l'accès depuis localhost. Une liste mal formée interrompt le démarrage

Endpoints

GET /health

Renvoie un statut de santé au format JSON. Utilisez-le pour les sondes de disponibilité et de vivacité de Kubernetes.

200 OK — tous les systèmes fonctionnent :

json
{ "status": "ok", "uptime_secs": 3612, "total_requests": 48203, "active_connections": 7, "executor_healthy": true, "plugins": { "otel": "ok" } }

503 Service Unavailable — un plugin a signalé une défaillance :

json
{ "status": "degraded", "uptime_secs": 3612, "total_requests": 48203, "active_connections": 7, "executor_healthy": true, "plugins": { "otel": "failed" } }

L'objet plugins liste chaque plugin chargé avec son statut de santé : "ok", "degraded" ou "failed". L'endpoint renvoie 503 lorsqu'un plugin signale "failed" ou lorsque l'exécuteur de scripts est défaillant (executor_healthy: false). Les plugins "degraded" apparaissent dans le corps JSON mais le statut HTTP reste 200.

GET /metrics

Renvoie des métriques compatibles Prometheus au format texte (text/plain; version=0.0.4; charset=utf-8). Renvoie toujours 200.

bash
curl http://localhost:9090/metrics
text
# HELP oxphp_requests_total Total HTTP requests # TYPE oxphp_requests_total counter oxphp_requests_total 48203 # HELP oxphp_active_connections Current open connections # TYPE oxphp_active_connections gauge oxphp_active_connections 7 ...

Pour la liste complète des métriques disponibles, voir Métriques.

GET /config

Renvoie la configuration active du serveur au format JSON. Renvoie toujours 200.

bash
curl -s http://localhost:9090/config | jq .
json
{ "listen_addr": "0.0.0.0:80", "document_root": "/var/www/html/public", "entry_file": "/var/www/html/public/index.php", "log_level": "info", "executor_type": "sapi", "php_workers": "8", "tokio_workers": 4, "queue_capacity": 1024, "max_connections": 10000, "drain_timeout_seconds": 30, "header_timeout_seconds": 5, "rate_limit": 100, "rate_window_seconds": 60, "tls_enabled": true, "tls_min_version": "1.2", "compression_level": 4, "access_log": "all", "max_query_body": 524288, "worker_mode_enabled": false, "worker_max_memory_mib": 0, "static_max_age": 2592000, "static_revalidate": false, "async_workers": 0, "async_queue_capacity": 0, "async_max_fibers": 256, "async_in_flight_cap": 0, "trace_context": false, "superglobals_enabled": true, "trusted_proxies": false, "plugins": {} }

Les chemins des fichiers de certificat et de clé TLS ne sont jamais émis (seul le booléen tls_enabled est exposé), et internal_addr ainsi que error_pages_dir sont expurgés de la réponse servie — topologie de déploiement et chemins du système de fichiers qui aideraient un attaquant et dont les collecteurs n'ont pas besoin.

Endpoints de plugins

Les plugins peuvent enregistrer des endpoints personnalisés sous le préfixe /__<plugin_name>/. Par exemple, un plugin nommé otel pourrait exposer /__otel/status. Ces endpoints ne sont disponibles que si le plugin correspondant est chargé et a enregistré un gestionnaire.

Tout chemin ne correspondant ni à un endpoint intégré ni à un endpoint de plugin renvoie 404 Not Found.

Intégration Kubernetes

Sonde de disponibilité

Utilisez /health pour déterminer si Kubernetes achemine le trafic vers le pod :

yaml
readinessProbe: httpGet: path: /health port: 9090 initialDelaySeconds: 2 periodSeconds: 5

Lorsque /health renvoie 503, Kubernetes retire le pod de la liste des endpoints du Service. Le trafic reprend lorsque l'endpoint renvoie de nouveau 200.

Sonde de vivacité

Utilisez le même endpoint pour redémarrer les pods qui deviennent non réactifs :

yaml
livenessProbe: httpGet: path: /health port: 9090 initialDelaySeconds: 5 periodSeconds: 10 failureThreshold: 3

Sonde de démarrage

Pour les applications au démarrage lent (frameworks volumineux, autoloaders lourds) :

yaml
startupProbe: httpGet: path: /health port: 9090 initialDelaySeconds: 1 periodSeconds: 2 failureThreshold: 15

Sécurité

Le serveur interne n'utilise aucune authentification par jeton bearer ; l'accès est contrôlé par l'adresse sur laquelle il se lie et par INTERNAL_ALLOW_IPS. Pour le protéger :

  • Liez-le à localhost — un INTERNAL_ADDR ne comportant qu'un port (:9090) se lie déjà à 127.0.0.1 ; un 127.0.0.1:9090 explicite maintient le port accessible uniquement depuis l'intérieur du conteneur ou de l'hôte
  • Définissez INTERNAL_ALLOW_IPS lorsque l'écouteur doit être accessible hors de l'hôte — une liste d'autorisation CIDR/IP qui renvoie 403 sur /metrics, /config et les chemins de plugins aux pairs qui n'en font pas partie, tandis que les sondes de santé restent accessibles. Le loopback n'est pas implicite, incluez donc 127.0.0.1/32 si vous avez encore besoin d'un accès depuis localhost. Le serveur émet un avertissement au démarrage si l'écouteur est exposé hors de l'hôte sans liste d'autorisation définie
  • Ne l'exposez pas en tant que Service Kubernetes — déclarez le port comme un containerPort mais ne créez pas de Service pour lui. Les sondes Kubernetes accèdent directement aux ports du conteneur
  • Utilisez des network policies — restreignez l'accès au niveau réseau si le port doit être exposé
Warning

L'endpoint /config révèle des détails opérationnels (document root, limites de débit, nombre de workers, valeurs des délais d'expiration). Les chemins TLS, internal_addr et error_pages_dir sont expurgés, mais demandez-vous si le reste devrait être accessible depuis l'extérieur du pod.

Exemple Docker

Pour un aperçu rapide, vous pouvez publier le port interne ; en production, liez le serveur interne à localhost et utilisez les sondes Kubernetes.

compose.yaml
services: app: image: ghcr.io/oxphp/oxphp:0.10.0 ports: - "80:80" - "9090:9090" environment: - DOCUMENT_ROOT=/var/www/html/public - INTERNAL_ADDR=0.0.0.0:9090

Dépannage

Les endpoints de santé, de métriques et de configuration ne sont pas accessibles

INTERNAL_ADDR n'est pas défini.

Correction : Ajoutez la variable d'environnement :

bash
INTERNAL_ADDR=0.0.0.0:9090
/health renvoie 503 mais l'application fonctionne

Un plugin chargé signale PluginHealth::Failed. Vérifiez l'objet plugins dans la réponse de santé pour identifier le plugin défaillant :

bash
curl -s http://localhost:9090/health | jq '.plugins'
Impossible d'atteindre le serveur interne depuis l'extérieur du conteneur

Le serveur est lié à 127.0.0.1, qui n'est accessible que depuis l'intérieur du conteneur.

Correction : Passez à 0.0.0.0 pour un accès externe, ou utilisez des sondes Kubernetes qui accèdent directement au conteneur.

Les métriques n'affichent pas le mode worker ou les métriques async

Les métriques du mode worker n'apparaissent que lorsque le mode worker est activé (WORKER_MODE_ENABLED=true). Les métriques async n'apparaissent que lorsque ASYNC_WORKERS > 0 et qu'au moins une tâche a été distribuée ou rejetée.

Voir aussi