docs/blog/release-v0.8.7.md
June 2026 - 7 min read
I'm releasing Crawl4AI v0.8.7, a security-hardening release. It bundles every responsibly-disclosed vulnerability we patched since v0.8.6, adds the new DomainMapper feature, and ships a batch of scraping, deep-crawl, and LLM fixes from our team and the community.
If you self-host the Docker API server, please upgrade right away. This release closes several critical issues, and two GitHub Security Advisories accompany it.
v0.8.7 is, first and foremost, a security release. Every issue below was reported responsibly by the community, and every reporter is credited in SECURITY-CREDITS.md and the published advisories.
gi_frame.f_back frame-chain walk escaped the computed-field expression sandbox to reach the real __import__. We removed eval() from computed fields entirely. SDK users can still pass Python callables via the function key.asyncio, json, re) carried a full __builtins__, providing an alternate path to __import__. We stripped those builtins and tightened the allowlist."mysecret". We removed the default, reject weak or short secrets at startup, and auto-generate an ephemeral key when JWT is enabled with no key set./monitor/* routes, including destructive actions, ran without auth. They now require a token, and the WebSocket endpoint checks the token explicitly./crawl/job and /llm/job could hit internal and cloud-metadata addresses. We added a blocklist and disabled redirect following./crawl, /md, and /llm fetched arbitrary URLs, and IPv6-mapped IPv4 addresses such as [::ffff:169.254.169.254] slipped past naive checks. We added destination validation on all entry points and normalize IPv6-mapped IPv4 before the blocklist check./screenshot and /pdf honored any output_path. Writes are now restricted to CRAWL4AI_OUTPUT_DIR, and .. traversal is rejected.innerHTML without escaping. We escape on both the server and the client now./execute_js (CVSS 8.1): the endpoint is disabled by default behind CRAWL4AI_EXECUTE_JS_ENABLED, we removed --disable-web-security from default browser args, and added an SSRF blocklist on the destination.We also replaced the eval() in /config/dump with Pydantic-validated JSON input, and added type validation for markdown_generator in CrawlerRunConfig.
DomainMapper discovers URLs across an entire domain in one pass, combining multiple discovery sources. It supports an include_subdomains flag to widen or narrow the crawl boundary, and a per-source timeout so a single slow source cannot stall the whole map. See the Domain Mapping guide.
The Docker API now accepts a list of configs aligned with the list of URLs, so you can apply a different CrawlerRunConfig to each URL in a single arun_many request (#1837).
Markdown and scraping
rowspan and colspan in cleaned HTML (#1920).tail text when removing empty elements (#1938)NlpSentenceChunking (#1909)Deep crawl and dispatcher
set(False) instead of reset(token) (#1917)semaphore_count into the auto-created MemoryAdaptiveDispatcher and default it to 10 (#1927)LLM and providers
LLMExtractionStrategy.extraction_type to schemaLLMTableExtraction to the Docker deserialization allowlistCrawler and downloads
success=True for binary downloads, and skip the block check when downloaded_files is set<base href> in prefetch quick_extract_links (#752)Logging and MCP
AsyncLogger output to stderr by default (#1968) and use Console(width=200) for non-TTY contextsensure_ascii=False in the MCP bridge to preserve CJK characters (#1967)Browser and misc
browser_adapter now uses the Stealth import, fixing a stealth import mismatch (#1960)arun() return type to CrawlResultContainer (#1898)pip install -U crawl4ai
Docker users should pull the latest image once the Docker release workflow finishes.
Thank you to the researchers who disclosed these issues responsibly: Song Binglin (q1uf3ng), by111 (August829), Jeongbean Jeon, wulonchia, secsys_codex, Velayutham Selvaraj, and IcySun. Full details are in SECURITY-CREDITS.md.