unclecode/crawl4ai · warning · HTTPException

execute_js endpoint is disabled. Set CRAWL4AI_EXECUTE_JS_ENA

Error message

execute_js endpoint is disabled. Set CRAWL4AI_EXECUTE_JS_ENABLED=true to enable.

What it means

A 403 from POST /execute_js: the endpoint is compile-time gated by the EXECUTE_JS_ENABLED flag (env CRAWL4AI_EXECUTE_JS_ENABLED) and is disabled by default, because letting clients run arbitrary JavaScript in the server's browser is dangerous. No request content can bypass this; only the environment variable enables it.

Source

Thrown at deploy/docker/server.py:786

            response_headers: Optional[dict] = None
            status_code: Optional[int] = None
            ssl_certificate: Optional[SSLCertificate] = None
            dispatch_result: Optional[DispatchResult] = None
            redirected_url: Optional[str] = None
            network_requests: Optional[List[Dict[str, Any]]] = None
            console_messages: Optional[List[Dict[str, Any]]] = None

        class MarkdownGenerationResult(BaseModel):
            raw_markdown: str
            markdown_with_citations: str
            references_markdown: str
            fit_markdown: Optional[str] = None
            fit_html: Optional[str] = None
        ```

    """
    if not EXECUTE_JS_ENABLED:
        raise HTTPException(403, "execute_js endpoint is disabled. Set CRAWL4AI_EXECUTE_JS_ENABLED=true to enable.")
    validate_url_scheme(body.url)
    # Block SSRF: reject internal/private IPs
    try:
        validate_webhook_url(body.url)  # reuse SSRF blocklist
    except ValueError as e:
        raise HTTPException(400, str(e))
    crawler = None
    try:
        cfg = CrawlerRunConfig(js_code=body.scripts)
        crawler = await get_crawler(get_default_browser_config())
        results = await crawler.arun(url=body.url, config=cfg)
        if not results[0].success:
            raise HTTPException(500, detail=results[0].error_message or "Crawl failed")
        data = results[0].model_dump()
        return JSONResponse(data)
    except Exception as e:
        raise HTTPException(500, detail=str(e))
    finally:

View on GitHub (pinned to 7e80152142)

Solutions

  1. Set CRAWL4AI_EXECUTE_JS_ENABLED=true in the container environment and restart, if you accept the risk of running client-supplied JS.
  2. If you don't need JS execution, use /crawl with js_code in crawler_config if that path is enabled, or other endpoints.
  3. Verify the flag took effect: docker inspect the env or check server startup logs.

Example fix

# before
docker run ... crawl4ai-server
# after
docker run -e CRAWL4AI_EXECUTE_JS_ENABLED=true ... crawl4ai-server
Defensive patterns

Strategy: validation

Validate before calling

import requests

def execute_js_available(base: str, hdrs: dict) -> bool:
    # cheap probe: the flag check runs before auth-heavy work
    r = requests.post(f'{base}/execute_js', json={'url': 'https://example.com', 'scripts': []}, headers=hdrs)
    return r.status_code != 403 or 'disabled' not in r.text

Try / catch

resp = requests.post(f'{BASE}/execute_js', json=body, headers=hdrs)
if resp.status_code == 403 and 'CRAWL4AI_EXECUTE_JS_ENABLED' in resp.text:
    raise RuntimeError('execute_js disabled server-side; set CRAWL4AI_EXECUTE_JS_ENABLED=true or use another endpoint')

Prevention

When it happens

Trigger: POST /execute_js against any deployment where CRAWL4AI_EXECUTE_JS_ENABLED is not set to 'true'. The check happens before URL validation, SSRF checks, and token-dependent crawl logic, so every call fails identically.

Common situations: New deployments using default env; upgrading the server to a version that added the kill switch; CI environments that never set the flag; MCP tool discovery listing execute_js even though it's off.

Related errors


AI-assisted analysis of unclecode/crawl4ai@7e80152142 (2026-08-14). Data as JSON: /api/errors/5509eeabbf61b35d. Report an issue: GitHub.