← Blog

BLOG

Playwright Chrome Extension Install: What to Do Instead

Playwright Chrome extension install isn't a real setup step. Learn what the extension actually is, and how to connect Playwright to Chrome with CDP.

September 16, 20269 min readRemote Browser

# Playwright Chrome Extension Install: What to Do Instead

If you searched for a Playwright Chrome extension install, you are probably trying to get Playwright to drive the Chrome window you already have open. There is no official Playwright Chrome extension to install. Playwright ships as an npm package plus browser binaries, and it controls browsers through the Chrome DevTools Protocol (CDP) or its own launcher — not through a browser extension.

This guide explains what people usually mean by "Playwright Chrome extension," why the install path does not exist, and the connection methods that actually work: connectOverCDP, launchOptions.args, and hosted Chromium sessions you can attach to from anywhere.

Why there is no Playwright Chrome extension to install

Playwright is a Node.js (and Python/.NET/Java) library. When you run npm i -D @playwright/test and npx playwright install, you get:

  • The Playwright client library in node_modules.
  • Browser binaries (Chromium, Firefox, WebKit) in a local cache directory.

Neither of those is a Chrome extension. Playwright talks to browsers over a debugging protocol. For Chromium-based browsers that protocol is CDP; for Firefox and WebKit, Playwright uses its own patched builds and a similar out-of-process control channel.

The confusion comes from a few places:

  • Chrome extensions that automate tabs. Tools like Selenium IDE or various "AI browser" extensions live inside Chrome and drive the active tab. That is a different architecture from Playwright.
  • Playwright MCP servers. The Playwright MCP project exposes browser control to LLM clients. It is an MCP server, not a Chrome extension, though some clients wrap it in a UI that looks extension-like.
  • Old Puppeteer-era tutorials. Puppeteer also has no Chrome extension; it connects over CDP. People search for the same thing under both names.

So the honest answer to "how do I install the Playwright Chrome extension" is: you don't. You install Playwright, then decide how it will reach a browser.

What you actually install

GoalWhat to installCommand / artifact
Run Playwright tests locallyPlaywright package + browsersnpm i -D @playwright/test then npx playwright install
Drive your own Chrome via CDPPlaywright package onlynpm i playwright, launch Chrome with --remote-debugging-port
Connect to a hosted browserPlaywright package onlychromium.connectOverCDP(wsEndpoint)
Give an LLM browser controlPlaywright MCP servernpx @playwright/mcp@latest
Automate from a Chrome extensionA different toolNot Playwright

If you only need to attach to a browser that already exists — local or remote — you do not need npx playwright install at all. The client library is enough. That is a meaningful difference when you are building CI images or agent containers and do not want a few hundred megabytes of browser binaries in every layer.

Connecting Playwright to an existing Chrome over CDP

This is the closest thing to what people imagine a "Chrome extension" would do: take the browser that is already running and drive it.

Start Chrome with a debugging port:

# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
  --remote-debugging-port=9222 \
  --user-data-dir=/tmp/chrome-profile

# Linux
google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-profile

Then connect from Playwright:

import { chromium, Browser, BrowserContext, Page } from 'playwright';

async function attachToChrome(): Promise<void> {
  // connectOverCDP returns a Browser bound to the running Chrome instance.
  const browser: Browser = await chromium.connectOverCDP(
    'http://127.0.0.1:9222'
  );

  // Existing tabs live in the default context. Reuse it; do not create a new one.
  const contexts: BrowserContext[] = browser.contexts();
  const context: BrowserContext = contexts[0] ?? (await browser.newContext());
  const pages: Page[] = context.pages();
  const page: Page = pages[0] ?? (await context.newPage());

  await page.goto('https://example.com');
  console.log(await page.title());

  // Do NOT call browser.close() here — it would kill the user's Chrome.
  await browser.close();
}

attachToChrome().catch((err) => {
  console.error('CDP connect failed:', err);
  process.exit(1);
});

Three things trip people up here:

  1. `connectOverCDP` is Chromium-only. It will not attach to Firefox or WebKit. If you need those, you must launch them through Playwright.
  2. `browser.close()` disconnects and can terminate the browser. If you attached to a user's session, close the connection without killing the process, or just let the script exit.
  3. The default context already exists. Calling newContext() on a CDP-connected browser creates an incognito-style context, not a second window in the same profile.

For a deeper walkthrough of the attach flow, see Playwright attach to existing browser.

When connectOverCDP fails

The most common failure is a connection refused or a WebSocket handshake error. Causes, roughly in order of frequency:

  • Chrome was already running. A second launch with --remote-debugging-port is ignored because the existing process owns the profile. Quit Chrome fully, or use a separate --user-data-dir.
  • The port is bound to localhost only. If Playwright runs in a container and Chrome runs on the host, 127.0.0.1 inside the container is not the host. Use host.docker.internal or a tunnel.
  • You are passing an HTTP URL where a WS URL is expected, or vice versa. connectOverCDP accepts both http://host:port and a full ws:// endpoint, but the endpoint must be the browser-level one, not a page target.
  • Chrome updated and changed the debugging surface. Rare, but pinning a Chrome version in CI avoids surprise.

If you are hitting browserWSEndpoint not working errors from Puppeteer-era code, the same diagnosis applies: the endpoint is either stale, wrong-scoped, or unreachable from the caller's network.

launchOptions.args: controlling the browser you launch

If you do not need to attach to an existing Chrome, launching your own with explicit args is more predictable. This is where launchOptions.args matters.

import { chromium } from 'playwright';

const browser = await chromium.launch({
  headless: true,
  args: [
    '--no-sandbox',
    '--disable-dev-shm-usage',
    '--disable-gpu',
    '--remote-debugging-port=0', // let the OS pick a free port
  ],
});

const context = await browser.newContext({
  viewport: { width: 1280, height: 800 },
  locale: 'en-US',
  timezoneId: 'America/New_York',
});

const page = await context.newPage();
await page.goto('https://example.com');
await browser.close();

Notes on the args above:

  • --no-sandbox is common in containers but weakens isolation. Only use it where the container itself is the security boundary.
  • --disable-dev-shm-usage avoids /dev/shm exhaustion in Docker, which otherwise causes random tab crashes.
  • --remote-debugging-port=0 lets the OS assign a port, which is useful when you want to expose the CDP endpoint to another process without hardcoding a number.

For a fuller treatment of launch flags and their trade-offs, see Playwright browser launch options.

Local Chrome vs hosted Chromium

Once you move past a single developer machine, the local-Chrome approach starts to cost more than it saves.

DimensionLocal Chrome + CDPHosted Chromium session
SetupInstall Chrome, manage profile dirs, open portsGet a CDP endpoint, connect
ScalingOne browser per machine, manualSessions provisioned per request
IsolationShared profile unless you script itSession-scoped by default
CI friendlinessNeeds a display or headless flagsHeadless by default
DebuggingLocal DevTools onlyLive viewer plus CDP
Profile persistenceManual --user-data-dir managementConfigurable persistent profiles
Network egressYour machine's IPConfigurable proxy settings

The trade-off is control versus operational overhead. Local Chrome gives you the exact build and flags you chose. A hosted runtime gives you a consistent, disposable browser you can reach from any worker, which is usually what you want once more than one process needs to drive a browser.

Remote Browser provides hosted Chromium sessions with CDP access, so the same connectOverCDP call works against a remote endpoint:

import { chromium } from 'playwright';

// wsEndpoint comes from your session provisioning call.
const browser = await chromium.connectOverCDP(process.env.BROWSER_WS_ENDPOINT!);
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://example.com');
await browser.close();

The client code is identical to the local case. What changes is where the browser lives and who is responsible for keeping it alive, patched, and isolated. See Remote Browser for AI agents for the runtime model, and Remote Browser online if you want to try a session without installing anything locally.

Playwright MCP and the "extension" confusion

The Playwright MCP server is the other thing people mean when they search for a Chrome extension. It exposes Playwright's browser control as MCP tools so an LLM client can call browser_navigate, browser_click, and so on.

It is not a Chrome extension. It is a process that owns a browser and speaks MCP to the client. You can point it at a hosted browser the same way you would point a script at one, which is often the right move for agents that need a stable, long-lived session rather than a browser tied to a developer's laptop.

If your actual goal is "let an agent drive a browser," the MCP route is usually more direct than trying to bolt automation onto a Chrome extension. For the connection-level details, see Playwright MCP Chrome extension.

Production criteria before you commit

Whichever path you pick, check these before you build on it:

  • Endpoint stability. Does the CDP endpoint survive a worker restart, or do you need to re-provision? Long-running agents need the latter.
  • Session isolation. Can two concurrent jobs share a profile by accident? They should not be able to.
  • Profile persistence. If your workflow logs in once and reuses cookies, you need a profile that outlives a single session.
  • Network controls. Proxy and egress settings matter for anything touching geo-restricted or rate-limited sites.
  • Observability. A live viewer plus CDP access makes debugging a failed run far cheaper than log archaeology.
  • Cost model. Browser time is metered differently across providers. Check pricing for current details rather than assuming a per-request model.

None of these are Playwright problems. They are runtime problems, and they are the reason teams move from "Chrome on my laptop" to a hosted browser once automation leaves the prototype stage.

Summary

There is no Playwright Chrome extension to install. Playwright is a library that controls browsers over CDP or its own protocol. If you want to drive an existing Chrome, launch it with --remote-debugging-port and call chromium.connectOverCDP(). If you want a browser you can reach from anywhere, connect the same client to a hosted Chromium endpoint and let the runtime handle isolation, profiles, and lifecycle.

The install step you were looking for does not exist. The connection step does, and it is one line of code. For the full API surface, see the documentation.