firecrawl/firecrawl · error · SSLError

SCRAPE_SSL_ERROR

SCRAPE_SSL_ERROR

Error message

An SSL/TLS certificate error occurred while trying to establish a secure connection to this website. This usually happens when a website has an expired, self-signed, or misconfigured SSL certificate. If you trust this website and are not submitting sensitive data, you can bypass this error by setting `skipTlsVerification: true` in your scrape request. Note: Only do this for trusted sites as it disables certificate validation.

What it means

SSLError is raised by the plain fetch engine when the underlying Node fetch fails with a TypeError whose message is 'fetch failed' and whose cause carries code === 'CERT_HAS_EXPIRED'. It surfaces an expired TLS certificate on the target site. The remedy is exposed to the caller via meta.options.skipTlsVerification, which the SSLError message tells the user to enable. Note only the expired-cert branch maps here; other TypeError causes (including InsecureConnectionError) are rethrown unchanged.

Source

Thrown at apps/api/src/scraper/scrapeURL/engines/fetch/index.ts:207

        headers: [...x.headers],
      };

      if (meta.mock === null) {
        await saveMock(mockOptions, response);
      }
    } catch (error) {
      if (
        error instanceof TypeError &&
        error.cause instanceof InsecureConnectionError
      ) {
        throw error.cause;
      } else if (
        error instanceof Error &&
        error.message === "fetch failed" &&
        error.cause &&
        (error.cause as any).code === "CERT_HAS_EXPIRED"
      ) {
        throw new SSLError(meta.options.skipTlsVerification);
      } else {
        throw error;
      }
    }
  }

  await specialtyScrapeCheck(
    meta.logger.child({ method: "scrapeURLWithFetch/specialtyScrapeCheck" }),
    Object.fromEntries(response.headers as any),
  );

  return {
    url: response.url,
    html: response.body,
    statusCode: response.status,
    contentType:
      (response.headers.find(x => x[0].toLowerCase() === "content-type") ??
        [])[1] ?? undefined,

View on GitHub (pinned to 656bffcc28)

Solutions

  1. Set skipTlsVerification: true in the scrape options if you trust the site and are not submitting sensitive data.
  2. Verify the site's certificate with curl -vI https://host and ask the site admin to renew if it is genuinely expired.
  3. Try the http:// URL instead if the site is reachable over plain HTTP.
  4. Correct an inaccurate system clock on the scraping host, which can cause valid certs to appear expired.

Example fix

// before
await scrapeURL({ url: 'https://internal-staging.example.com/data' });

// after — skip TLS verification for a trusted internal site
await scrapeURL({ url: 'https://internal-staging.example.com/data', skipTlsVerification: true });
Defensive patterns

Strategy: validation

Validate before calling

async function probeTls(url) {
  try {
    const res = await fetch(url, { method: 'HEAD' });
    return { ok: true, status: res.status };
  } catch (e) {
    if (e?.cause?.code === 'CERT_HAS_EXPIRED') return { ok: false, reason: 'expired-cert' };
    return { ok: false, reason: String(e?.message ?? e) };
  }
}

Type guard

import { SSLError } from '../error';
function isSSLError(e) {
  return e instanceof SSLError || (e instanceof Error && e.name === 'SSLError');
}

Try / catch

try {
  await scrapeURL({ url });
} catch (e) {
  if (isSSLError(e) && isTrustedInternal(url)) {
    await scrapeURL({ url, skipTlsVerification: true });
  } else throw e;
}

Prevention

When it happens

Trigger: A scrape that falls through to the fetch engine (no Chrome required) against an https URL whose certificate has expired. Node's undici fetch throws TypeError('fetch failed') with error.cause.code === 'CERT_HAS_EXPIRED'; the catch at fetch/index.ts:195-210 matches it and throws SSLError(skipTlsVerification). InsecureConnectionError causes are rethrown as-is and do not become SSLError.

Common situations: Scraping a small/stale site whose cert lapsed; internal/staging domains with self-signed certs that also report as expired; a site mid-renewal; CI where the runner clock is wrong and treats a valid cert as expired. Developers often hit this when moving from a browser-tolerant scrape to the lightweight fetch path.

Understand the failure class

Related errors


AI-assisted analysis of firecrawl/firecrawl@656bffcc28 (2026-08-12). Data as JSON: /api/errors/f034ffcc59b5ee4b. Report an issue: GitHub.