Short answer: PhantomJS is not a supported, current Firebase Functions runtime. A new implementation should use Puppeteer with a supported Node.js runtime (currently Node.js 20 or 22 in Firebase documentation), or call an external rendering service. Keep PhantomJS only for unavoidable legacy maintenance, package its binary as an unverified executable, and expect migration work around compatibility, deployment size, and runtime lifecycle.
What Firebase Functions supports today
Firebase Functions projects can use JavaScript, TypeScript, or Python and can expose HTTPS, callable, task-queue, scheduled, and background-triggered handlers. For browser automation, a Node.js function is the practical choice because Puppeteer provides a JavaScript API for Headless Chrome.
Set the runtime explicitly and recheck Firebase’s runtime-management documentation before every major deployment. Firebase currently documents Node.js 20 and Node.js 22; Node.js 18 is deprecated, while Node.js 14 and 16 were decommissioned in early 2025. Google Cloud’s lifecycle table lists Node.js 22 decommissioning on 2027-10-31. These dates can change, so treat them as operational data rather than permanent guarantees.
The older functions.config() configuration API is scheduled for decommissioning in March 2027. New code should use parameterized configuration instead.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →#1 Best Overall
Why historical PhantomJS recipes fail
Older tutorials commonly download a PhantomJS binary, call it with Node’s child_process, and deploy with an old Node version. That approach is fragile for several independent reasons:
- Firebase documents supported Node.js runtimes, not PhantomJS as a managed browser option.
- The executable must match the production Linux environment, have executable permissions, and fit deployment packaging limits.
- Old PhantomJS JavaScript and browser standards do not match modern sites.
- Runtime decommissioning can break a previously deployable function even when the script itself is unchanged.
- Launching a separate binary complicates cold starts, timeout handling, cleanup, and local-versus-production debugging.
These are engineering risks, not a claim that every legacy deployment will fail. If a business-critical script cannot yet be migrated, isolate it behind a separately managed container or browser-rendering API and let the Firebase function call that service.
Recommended migration: Puppeteer in Firebase Functions
1. Initialize the project
- Install the Firebase CLI, authenticate, and run
firebase init functions. - Choose JavaScript or TypeScript and select a supported Node.js runtime.
- From the functions directory, install Puppeteer with
npm install puppeteer. Keep the dependency in the deployed functions package, not only in a parent workspace.
Puppeteer’s troubleshooting documentation states that the Node.js runtime of Google Cloud Functions includes the system packages needed to run Headless Chrome. Puppeteer still downloads or resolves a compatible browser during installation according to its package configuration; verify the resulting deployment artifact in your own CI process.
2. Declare a supported runtime and resources
In functions/package.json, set the Node engine to a currently supported version, for example:
{
"engines": { "node": "22" },
"dependencies": {
"firebase-functions": "latest",
"firebase-admin": "latest",
"puppeteer": "latest"
}
}
Alternatively, set runtime options in your function configuration. Browser work normally needs more memory and a longer timeout than a JSON-only endpoint. Choose region, memory, timeout, minimum instances, maximum instances, and concurrency deliberately; high concurrency can exhaust memory when several Chromium processes or pages run together.
3. Implement a complete HTTPS function
This JavaScript example launches one browser per invocation, waits for a usable page, returns extracted HTML, and closes the browser even when navigation fails:
const { onRequest } = require("firebase-functions/v2/https");
const puppeteer = require("puppeteer");
exports.render = onRequest(
{ region: "us-central1", timeoutSeconds: 120, memory: "1GiB", maxInstances: 10 },
async (req, res) => {
const target = typeof req.query.url === "string" ? req.query.url : "https://example.com";
let browser;
try {
const parsed = new URL(target);
if (!["http:", "https:"].includes(parsed.protocol)) {
return res.status(400).send("Only http and https URLs are allowed");
}
browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.setViewport({ width: 1365, height: 900, deviceScaleFactor: 1 });
await page.goto(parsed.href, { waitUntil: "networkidle2", timeout: 90000 });
const title = await page.title();
const html = await page.content();
return res.json({ title, html });
} catch (error) {
console.error("render failed", error);
return res.status(502).json({ error: "Page rendering failed" });
} finally {
if (browser) await browser.close().catch((closeError) => console.error("browser close failed", closeError));
}
}
);
Do not accept arbitrary internal URLs in a public endpoint. Validate schemes, restrict destinations when possible, and consider authentication and rate limiting to reduce server-side request forgery risk. Never put secrets in query strings that will be logged; use parameterized configuration or a secret manager.
4. Test with the Local Emulator Suite
Start the emulators with firebase emulators:start, call the local HTTPS endpoint, and test slow pages, redirects, invalid URLs, JavaScript errors, and browser shutdown. The emulator lets you inspect logs and iterate without deploying every change. Exercise the same memory and timeout assumptions in a staging project because local hardware is not production Cloud Functions.
5. Deploy
firebase deploy --only functions
After deployment, inspect invocation logs and response latency. A browser function can spend most of its time in cold-start browser startup and page loading, so set client and function timeouts consistently.
Translating PhantomJS APIs to Puppeteer
| PhantomJS pattern | Puppeteer equivalent | Migration note |
|---|---|---|
page.open(url, callback) |
await page.goto(url, options) |
Check the returned response and choose an explicit waitUntil condition. |
page.render(path) |
await page.screenshot({path, fullPage:true}) |
Set viewport and device scale explicitly for repeatable output. |
page.evaluate(...) |
await page.evaluate(...) |
Review serialization and asynchronous browser code. |
page.onResourceRequested |
page.on('request', handler) |
Call request.continue(), abort(), or respond() deliberately. |
| PhantomJS user-agent settings | page.setUserAgent(value) |
Use a modern, truthful user agent unless a target explicitly requires another one. |
Do not translate callbacks mechanically. Replace callback chains with async/await, add explicit navigation and selector timeouts, and close every page and browser in cleanup code.
If PhantomJS absolutely cannot be migrated
Treat PhantomJS as an unsupported legacy executable, not as a Firebase feature. Package the exact binary and script, verify executable permissions, and confirm that the binary matches the deployed Linux architecture. Invoke it with a strict timeout and kill the process on timeout or function cancellation. Capture stdout and stderr, delete temporary files, and return a controlled error instead of waiting indefinitely.
Keep this path behind an authenticated endpoint or private trigger. A separately managed container is safer because you control its operating-system libraries and can pin the old binary independently of Firebase runtime upgrades. The Firebase function can submit a job to that service and return a status or callback rather than holding an HTTPS invocation open.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Troubleshooting
“Cannot find module puppeteer”
Install Puppeteer in the deployed functions directory and commit the lockfile. Confirm that production installation is not omitting the dependency as a development-only package.
Browser executable or permission errors
Check the installed Puppeteer version, generated browser cache, and deployment artifact. Do not assume a PhantomJS binary or a local Chrome path exists in Cloud Functions. Use the browser supplied by your Puppeteer setup, or explicitly configure a verified executable path.
Navigation timeout
Use a realistic timeout, select waitUntil according to the page, and wait for a specific selector when the page has long-lived analytics connections. Log the target hostname and elapsed stages without logging credentials.
Rank #4
Out-of-memory or crashed Chromium
Increase memory, reduce viewport and page count, avoid parallel browsers, and set a maximum instance count. Close pages in a finally block. Repeated crashes indicate that the workload belongs in a container or specialized rendering service.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Works locally but not after deployment
Compare Node versions, environment variables, network access, region, and package contents. Reproduce with the Local Emulator Suite, then test in a staging Firebase project using production-like memory and timeout settings.
PhantomJS script shows a blank or broken page
Modern client-side frameworks may depend on browser APIs PhantomJS does not implement. Port the page logic to Puppeteer, or retain the old script behind an external service while migration is planned.
Performance, reliability, and cost decisions
- Cold starts: launching Chromium adds latency. A small minimum-instance setting can reduce cold starts but may increase cost.
- Timeouts: bound navigation, selector waits, and external calls separately so one stalled resource cannot consume the entire invocation.
- Concurrency: limit concurrent browser work per instance; memory, not CPU alone, is usually the constraint.
- Caching: cache stable results outside the function when freshness permits rather than rendering the same page for every request.
- Observability: record duration, navigation outcome, HTTP status, and sanitized error categories. Avoid storing page content or cookies unless required.
- Lifecycle: schedule runtime upgrades before Firebase or Google Cloud decommission dates force an emergency migration.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. It accepts a URL and returns PNG, JPEG, WebP, or PDF without requiring you to package Chromium in Firebase. Cookie and consent banners, newsletter popups, and chat widgets are removed before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing result.
One GET request is enough:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for options such as full-page capture, CSS selectors, device presets, retina scale, PDF output, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, geolocation, signed links, asynchronous jobs, bulk capture, caching, and usage reporting. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools to Claude, Cursor, and other MCP clients.
Free tools Windows power users keep installed
One-click scans. No signup required.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan. Create a free ScreenshotNeo account.
Best Value
FAQ
Can I select PhantomJS as a Firebase runtime?
No. Firebase’s documented runtime selection is for supported Node.js (and other supported language) environments, not PhantomJS.
Should I rewrite every PhantomJS test at once?
No. Prioritize production paths, create Puppeteer equivalents beside the old scripts, compare outputs, then retire each legacy path after validation.
Is an external renderer always necessary?
No. Puppeteer is suitable for many browser tasks directly in Functions. An external service becomes attractive when binary maintenance, long rendering jobs, or unpredictable memory use dominate the workload.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsFrequently Asked Questions
Can I select PhantomJS as a Firebase runtime?
No. Firebase’s documented runtime selection is for supported Node.js (and other supported language) environments, not PhantomJS.
Should I rewrite every PhantomJS test at once?
No. Migrate incrementally: run Puppeteer beside the old script, compare outputs, then retire each legacy path.
When should rendering move outside Firebase Functions?
Move it to a container or rendering service when browser binaries, long jobs, or memory variability make function operations unreliable.
The Bottom Line
For new Firebase browser automation, use Puppeteer on a supported Node.js runtime. Keep PhantomJS only as a controlled legacy bridge, or replace the entire browser layer with a rendering service such as ScreenshotNeo.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

