Reads nginx access/error logs directly from a mounted NPM log directory, enabling agents to debug proxy issues without SSH access. Requires mounting NPM's /data/logs volume and setting NPM_LOG_DIR. Also includes a feature request PRD for proposing a native log API upstream.
6.8 KiB
Feature Request: Per-Host Log Retrieval API
Project: NginxProxyManager/nginx-proxy-manager Type: Feature Request Status: Draft PRD
Problem Statement
Nginx Proxy Manager writes per-host access and error logs to predictable paths on disk
(/data/logs/proxy-host-{id}_access.log, /data/logs/proxy-host-{id}_error.log), but
provides no API to read them. The only log-related API is the audit log (GET /api/audit-log),
which tracks admin configuration changes — not HTTP traffic.
This means operators and automation tools have no programmatic way to:
- Retrieve recent access log entries for a specific proxy host
- Check error logs when debugging upstream connectivity issues
- Monitor traffic patterns or detect anomalies through the existing API surface
The only workarounds today are direct filesystem access (requiring volume mounts or
docker exec) or external log aggregation pipelines, both of which add significant
operational complexity for a task that should be simple.
Proposed Solution
Add REST API endpoints to retrieve nginx access and error logs for individual proxy hosts.
New Endpoints
GET /api/nginx/proxy-hosts/{id}/logs
Retrieve log entries for a specific proxy host.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
type |
string | "access" |
Log type: "access" or "error" |
lines |
int | 100 |
Number of most recent lines to return (max: 1000) |
search |
string | - | Filter lines containing this substring |
since |
string | - | ISO 8601 timestamp — only return lines after this time |
Response (200):
{
"host_id": 5,
"log_type": "access",
"file": "proxy-host-5_access.log",
"total_lines": 4821,
"returned_lines": 100,
"lines": [
"[01/Jun/2025:14:22:31 +0000] HIT 200 200 - GET https app.example.com \"/api/data\" [Client 10.0.0.1] [Length 1542] [Gzip -] [Sent-to 192.168.1.50] \"Mozilla/5.0\" \"https://app.example.com/\"",
"..."
]
}
Error Responses:
| Status | Condition |
|---|---|
| 404 | Proxy host not found |
| 404 | Log file does not exist |
| 403 | User lacks permission for this host |
GET /api/nginx/proxy-hosts/{id}/logs/summary
Return a statistical summary of recent traffic for a proxy host.
Response (200):
{
"host_id": 5,
"period": "last_1000_lines",
"status_codes": {"200": 812, "301": 45, "404": 23, "500": 3},
"top_paths": ["/api/data", "/", "/login"],
"top_clients": ["10.0.0.1", "10.0.0.5"],
"cache_hit_rate": 0.42,
"access_log_size_bytes": 524288,
"error_log_size_bytes": 8192
}
Permissions
| Permission | Description |
|---|---|
proxy-hosts:logs |
Read logs for proxy hosts the user can view |
Admin users can read logs for any host. Non-admin users can only read logs for hosts they own, consistent with existing proxy host permissions.
Backend Implementation Notes
The implementation is straightforward because log paths are already deterministic and hardcoded in nginx templates:
// backend/templates/proxy_host.conf
access_log /data/logs/proxy-host-{{ id }}_access.log proxy;
error_log /data/logs/proxy-host-{{ id }}_error.log warn;
A minimal implementation would:
- Add a new route in
backend/routes/(e.g.,proxy-host-logs.js) - Verify the proxy host exists and the user has access
- Read the last N lines from the log file using a reverse-reader (or
tail-like approach) - Optionally filter lines by substring or timestamp
- Return as JSON
Reference files for implementation:
backend/routes/nginx/proxy_hosts.js— existing proxy host routes and permission modelbackend/templates/proxy_host.conf— log path template confirming the naming conventionbackend/lib/access/— permission definition filesdocker/rootfs/etc/logrotate.d/nginx-proxy-manager— log rotation config (rotated logs have.1,.2.gzsuffixes)
Log Rotation Consideration
NPM rotates logs weekly (access: 4 rotations, error: 10 rotations). The API should
read only the current (unrotated) log file. Rotated archives (.1, .2.gz) could be
supported in a future iteration but are not required for the initial implementation.
Motivation
Use Cases
-
MCP/AI Agent Integration — MCP servers wrapping the NPM API (like nginx-proxy-manager-mcp) can expose log retrieval to AI assistants for debugging proxy issues conversationally.
-
Quick Debugging — When a reverse proxy returns errors, operators need to quickly check the access and error logs for that specific host. Today this requires SSH/exec access to the container.
-
Monitoring Dashboards — Custom dashboards can poll the log endpoint for traffic summaries without deploying a full log aggregation stack.
-
Automation — CI/CD pipelines and health-check scripts can verify that traffic is flowing correctly to newly deployed services behind NPM.
Why Not External Log Aggregation?
External solutions (Loki, ELK, Fluentd) are powerful but heavy. Many NPM users run single-host homelab setups where a full log pipeline is disproportionate to the need. A built-in API covers 80% of use cases with zero additional infrastructure.
Alternatives Considered
| Approach | Pros | Cons |
|---|---|---|
| API endpoint (proposed) | Native, zero extra infra | Requires upstream PR |
| Volume mount + file read | Works today, no NPM changes | Tight coupling, no access control, not portable |
| Docker exec | Works today | Requires Docker socket, security risk |
| Sidecar log server | Decoupled | Extra container, extra config, extra maintenance |
Scope
In Scope (v1)
GET /api/nginx/proxy-hosts/{id}/logswithtype,lines,searchparams- Permission checks consistent with existing proxy host access model
- Current (unrotated) log file only
Out of Scope (Future)
- Log streaming via WebSocket or SSE
- Rotated log archive access (
.gzfiles) - Log summary/analytics endpoint
- Redirection host, dead host, and stream logs (same pattern, easy to add later)
- Log download as file attachment
- Log retention configuration via API