File: test-webserver.md | Updated: 11/18/2025
On this page
Introduction
Playwright comes with a webserver option in the config file which gives you the ability to launch a local dev server before running your tests. This is ideal for when writing your tests during development and when you don't have a staging or production url to test against.
Configuring a web server
Use the webserver property in your Playwright config to launch a development web server during the tests.
playwright.config.ts
import { defineConfig } from '@playwright/test';export default defineConfig({ // Run your local dev server before starting the tests webServer: { command: 'npm run start', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, stdout: 'ignore', stderr: 'pipe', },});
| Property | Description |
| --- | --- |
| testConfig.webServer | Launch a development web server (or multiple) during the tests. |
| command | Shell command to start the local dev server of your app. |
| cwd | Current working directory of the spawned process, defaults to the directory of the configuration file. |
| env | Environment variables for the command. Defaults to inheriting process.env with PLAYWRIGHT_TEST=1 added. |
| gracefulShutdown | How to shut down the process. If unspecified, the process group is forcefully SIGKILLed. If set to { signal: 'SIGTERM', timeout: 500 }, the process group is sent a SIGTERM signal, followed by SIGKILL if it doesn't exit within 500ms. You can also use SIGINT as the signal instead. A 0 timeout means no SIGKILL will be sent. Windows doesn't support SIGTERM and SIGINT signals, so this option is ignored on Windows. Note that shutting down a Docker container requires SIGTERM. |
| ignoreHTTPSErrors | Whether to ignore HTTPS errors when fetching the url. Defaults to false. |
| name | Specifies a custom name for the web server. This name will be prefixed to log messages. Defaults to [WebServer]. |
| reuseExistingServer | If true, it will re-use an existing server on the url when available. If no server is running on that url, it will run the command to start a new server. If false, it will throw if an existing process is listening on the url. To see the stdout, you can set the DEBUG=pw:webserver environment variable. |
| stderr | Whether to pipe the stderr of the command to the process stderr or ignore it. Defaults to "pipe". |
| stdout | If "pipe", it will pipe the stdout of the command to the process stdout. If "ignore", it will ignore the stdout of the command. Default to "ignore". |
| timeout | How long to wait for the process to start up and be available in milliseconds. Defaults to 60000. |
| url | URL of your http server that is expected to return a 2xx, 3xx, 400, 401, 402, or 403 status code when the server is ready to accept connections. |
Adding a server timeout
Webservers can sometimes take longer to boot up. In this case, you can increase the timeout to wait for the server to start.
playwright.config.ts
import { defineConfig } from '@playwright/test';export default defineConfig({ // Rest of your config... // Run your local dev server before starting the tests webServer: { command: 'npm run start', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, timeout: 120 * 1000, },});
Adding a baseURL
It is also recommended to specify the baseURL in the use: {} section of your config, so that tests can use relative urls and you don't have to specify the full URL over and over again.
When using page.goto()
, page.route()
, page.waitForURL()
, page.waitForRequest()
, or page.waitForResponse()
it takes the base URL in consideration by using the URL()
constructor for building the corresponding URL. For Example, by setting the baseURL to http://localhost:3000 and navigating to /login in your tests, Playwright will run the test using http://localhost:3000/login.
playwright.config.ts
import { defineConfig } from '@playwright/test';export default defineConfig({ // Rest of your config... // Run your local dev server before starting the tests webServer: { command: 'npm run start', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, }, use: { baseURL: 'http://localhost:3000', },});
Now you can use a relative path when navigating the page:
test.spec.ts
import { test } from '@playwright/test';test('test', async ({ page }) => { // This will navigate to http://localhost:3000/login await page.goto('./login');});
Multiple web servers
Multiple web servers (or background processes) can be launched simultaneously by providing an array of webServer configurations. See testConfig.webServer
for more info.
playwright.config.ts
import { defineConfig } from '@playwright/test';export default defineConfig({ webServer: [ { command: 'npm run start', url: 'http://localhost:3000', name: 'Frontend', timeout: 120 * 1000, reuseExistingServer: !process.env.CI, }, { command: 'npm run backend', url: 'http://localhost:3333', name: 'Backend', timeout: 120 * 1000, reuseExistingServer: !process.env.CI, } ], use: { baseURL: 'http://localhost:3000', },});