{"record":{"id":"39a4081388398e15","repo":"opendatalab/MinerU","slug":"publicly-exposed-api-disables-http-client-backen","errorCode":null,"errorMessage":"Publicly exposed API disables *-http-client backends and server_url by default. Rebind to 127.0.0.1 or start with --allow-public-http-client if you understand the SSRF risk.","messagePattern":"Publicly exposed API disables \\*-http-client backends and server_url by default\\. Rebind to 127\\.0\\.0\\.1 or start with --allow-public-http-client if you understand the SSRF risk\\.","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"mineru/cli/public_http_client_policy.py","lineNumber":37,"sourceCode":"    *,\n    public_bind_exposed: bool,\n    allow_public_http_client: bool,\n) -> None:\n    app.state.public_bind_exposed = public_bind_exposed\n    app.state.allow_public_http_client = allow_public_http_client\n\n\ndef validate_public_http_client_request(\n    *,\n    public_bind_exposed: bool,\n    allow_public_http_client: bool,\n    backend: str,\n    server_url: str | None,\n) -> None:\n    if not public_bind_exposed or allow_public_http_client:\n        return\n    if backend.endswith(\"-http-client\") or bool(server_url and server_url.strip()):\n        raise HTTPException(status_code=400, detail=PUBLIC_HTTP_CLIENT_DISABLED_DETAIL)\n\n\ndef warn_if_public_http_client_policy(\n    *,\n    service_name: str,\n    host: str,\n    allow_public_http_client: bool,\n) -> None:\n    if not is_public_bind_host(host):\n        return\n    if allow_public_http_client:\n        logger.warning(\n            \"MinerU {} is listening on {} with --allow-public-http-client enabled. \"\n            \"Requests may supply remote HTTP inference endpoints and turn the service \"\n            \"into an externally driven outbound request primitive, creating SSRF and \"\n            \"internal network probing risk.\",\n            service_name,\n            host,","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/opendatalab/MinerU/blob/4fe4bde114a23ee5dd637eae99b767f4669bf58c/mineru/cli/public_http_client_policy.py#L19-L55","documentation":"HTTP 400 (PUBLIC_HTTP_CLIENT_DISABLED_DETAIL) raised by validate_public_http_client_request in public_http_client_policy.py: the API is bound to a public interface (not 127.0.0.1/localhost) AND --allow-public-http-client was not passed, AND the request tried to use a backend ending in '-http-client' or a non-empty server_url. This is a deliberate SSRF guard: a publicly exposed endpoint that lets callers choose an arbitrary server_url could be used to probe/attack internal network services, so http-client backends are disabled until the operator explicitly opts in.","triggerScenarios":"Starting the API with host 0.0.0.0 (or any LAN/public address) without --allow-public-http-client, then sending a request whose backend is e.g. 'vlm-http-client' or that carries a server_url pointing at a remote VLM service; Docker deployments binding 0.0.0.0; reverse-proxied setups where the bind looks public to the policy check.","commonSituations":"Containerizing the API (Docker defaults to 0.0.0.0) and wondering why server_url requests suddenly 400; exposing the service on a LAN for a team; moving from localhost dev to server deployment without re-reading the flags.","solutions":["If you accept the SSRF risk and run in a trusted network, restart the API with --allow-public-http-client.","Safer: bind to 127.0.0.1 and put an authenticating reverse proxy in front, so the policy never triggers.","Drop server_url / '-http-client' backends from requests and use locally hosted models instead.","Restrict at the network level (firewall/egress rules) if you must enable http-client backends publicly."],"exampleFix":"# before\nuvicorn mineru.cli.fast_api:app --host 0.0.0.0 --port 8000\n# POST with server_url='http://10.0.0.5:8001/vlm' -> 400 SSRF guard\n\n# after\nuvicorn mineru.cli.fast_api:app --host 0.0.0.0 --port 8000 --allow-public-http-client\n# (or) --host 127.0.0.1 behind an auth'd nginx proxy","handlingStrategy":"validation","validationCode":"def request_allowed(public_bind_exposed: bool, allow_public_http_client: bool,\n                  backend: str, server_url: str | None) -> bool:\n    if not public_bind_exposed or allow_public_http_client:\n        return True\n    return not backend.endswith('-http-client') and not (server_url and server_url.strip())","typeGuard":"def uses_http_client(backend: str, server_url: str | None) -> bool:\n    return backend.endswith('-http-client') or bool(server_url and server_url.strip())","tryCatchPattern":"r = requests.post(f'{base}/file_parse', json=payload)\nif r.status_code == 400 and 'http-client' in r.text:\n    raise RuntimeError('SSRF guard active: bind 127.0.0.1, or restart API with --allow-public-http-client')","preventionTips":["Default API binds to 127.0.0.1; keep it that way behind an authenticating reverse proxy for public exposure.","Only pass --allow-public-http-client after adding network egress controls — it reopens SSRF to your internal network.","In containers, prefer publishing the port (-p 127.0.0.1:8000:8000) over binding 0.0.0.0 unless required."],"tags":["mineru","ssrf","security","http-400","deployment"],"backgroundTag":null,"analyzedSha":"4fe4bde114a23ee5dd637eae99b767f4669bf58c","analyzedAt":"2026-08-14T21:29:18.456Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}