How to fix net::ERR_CONNECTION_REFUSED in Playwright navigation?
Playwrightnet::ERR_CONNECTION_REFUSED means the browser attempted a TCP connection to the target host and port, and the operating system immediately rejected it — nothing is listening there. In Playwright tests this almost always means either the local dev server hasn't started before tests run, the port is wrong, or a remote staging host is not reachable from the machine executing the tests. It is one of the most common failures when first setting up a local test suite.
Common mistake
// playwright.config.ts — no webServer configured
export default {
use: { baseURL: 'http://localhost:3000' },
};
// test.spec.ts
test('home page loads', async ({ page }) => {
// If the dev server isn't already running, this throws ERR_CONNECTION_REFUSED
await page.goto('/');
});
Running npx playwright test without ensuring the application is up first produces this error reliably in a fresh CI environment.
The fix
Use Playwright's webServer config to start and wait for the server before any tests run:
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
baseURL: 'http://127.0.0.1:3000',
},
webServer: {
command: 'npm run start',
url: 'http://127.0.0.1:3000',
reuseExistingServer: !process.env.CI,
timeout: 120 * 1000,
stdout: 'pipe',
stderr: 'pipe',
},
});
reuseExistingServer: !process.env.CI lets local developers keep their running dev server, while CI always starts a clean one. The url field is polled until the server responds, so tests only start once the application is accepting connections.
For staging/production targets that require network access:
// playwright.config.ts
export default defineConfig({
use: {
baseURL: process.env.BASE_URL ?? 'https://staging.example.com',
},
});
Ensure the CI runner's network can reach the staging host and that firewall rules permit outbound HTTPS from the runner IP.
Why it works
The webServer.url setting makes Playwright poll that endpoint with a HEAD request before releasing the test runner. This eliminates the race condition where test workers start before the application has bound to its port. Using 127.0.0.1 explicitly (rather than localhost) avoids IPv6 resolution order issues on some Linux environments where localhost resolves to ::1 but the server binds only on IPv4.
Tips
- Confirm the correct port: ERR_CONNECTION_REFUSED on port 3000 when the server listens on port 3001 is a common typo.
- In Docker-based CI, use host.docker.internal or the container's service hostname if the app runs in a separate container from the Playwright runner.
- If the error appears intermittently and only under parallel workers, the server may be overwhelmed — add a timeout large enough to accommodate slow cold starts.
- For DNS-based failures where the hostname doesn't resolve at all, see ERR_NAME_NOT_RESOLVED.