How to Quickly Test Websites with PHP’s Built-in Web Server

Need to quickly start a web server to test a PHP application? The PHP interpreter has one built in! You can use it to quickly inspect your work without running Apache, NGINX, or a containerization solution.

PHP’s built-in server receives relatively little attention but is powerful enough for development purposes. In this guide, we’ll show how you can use it as an alternative to other micro-servers like Python’s SimpleHTTPServer or the http-server npm package, which can’t run PHP scripts.

Using the Integrated Server

The built-in server is a convenient mechanism to help you test PHP sites in environments that lack a full-fledged HTTP server. It is available in PHP 5.4 and all later versions. You can run it directly from your working directory without having to set up a virtual host first.

Before using the server, be aware that it is designed for development purposes only. The PHP documentation explicitly warns against deploying this server in front of production applications. It is not secure enough to be exposed on publicly accessible networks.

Starting the server

The server is started by passing the -S flag at the php executable:

$ php -S localhost:8080
[Fri Jun 10 16:00:00 2022] PHP 8.1.5 Development Server (http://localhost:8080) started

The argument given to the command specifies the listening address of the server. We used the port 8080 on localhost in the example above. You can now visit http://localhost:8080 in your web browser to access the contents of your working directory. All PHP scripts will be executed automatically when you request them.

You can serve a path that is outside your working directory by setting the -t flag at server startup:

$ php -S localhost:8080 -t /home/$USER/public_docs

The document root will now be /public_docs in your personal folder.

Keep your terminal window open while using the web server. Press Ctrl + C to kill the process once you are done testing your site. PHP will log every incoming request to your terminal window, including the URI and HTTP method. Any undetected PHP errors will also appear in the logs.

Enabling remote access

Listen on localhost will not allow incoming connections from other devices on your network. You can allow remote access by binding to In place:

$ php -S

Remember that the server is not hardened for production use and should not be publicly exposed. Allow remote access only when absolutely necessary, such as when testing a particular feature on a mobile device. Make sure the port you are using is not open to the internet.

Request a relief match

PHP will search index.php and index.html files in the current document root when the incoming request does not contain a URI component. If none of these files exist, the server will continue to work its way up the directory tree, looking for an index in one of your document root’s parents. This means that you may unintentionally stream content that is outside of the directory you specified. A 404 Not Found status will be issued when the top of the tree is reached without an index file being found.

Requests that include a URI (like /file) must exactly match a static file in the document root. Otherwise, a 404 will be returned. PHP automatically sets the Content-Type response header to the MIME type of the served file for the most common file extensions.

Using a Router Script

You can optionally configure the web server to call a script on every request. This allows you to use your application’s front controller to perform advanced dynamic routing.

Router functionality is enabled by providing a PHP filename on the command line when you start the server:

$ php -S localhost:8080 router.php

PHP will now use router.php to manage all incoming request. You can route users to the appropriate point in your application by inspecting the request URI:

if ($_SERVER["REQUEST_URI"] === "/dashboard") {
else if ($_SERVER["REQUEST_URI"] === "/profile") {
else {

The output produced by your router script will become the response returned to the client. An exception is if the script returns false: in this case, PHP will return the static file that matches the original request URI.

if (str_starts_with($_SERVER["REQUEST_URI"], "/api")) {
    // Route to the correct API endpoint
    // ... 
else {
    // Serve other routes statically
    return false;

Embedded server detection from your PHP code

Your PHP code can detect if it is being called by the embedded web server by inspecting the name of the active interface. The php_sapi_name() function provides this value. It will be set to cli-server when the script was called by the integrated server component.

if (php_sapi_name() === "cli-server") {

Simultaneous processing of several requests

The server runs in single-process synchronous mode by default. Requests are handled individually and prevent each other from running until they are completed. This is one of the reasons why the server is not suitable for production use.

PHP 7.4 added support for handling multiple requests simultaneously. It relies on fork() availability and does not work on Windows. The server will fork a new worker to serve each incoming request when this mode is enabled. You can enable it by setting the PHP_CLI_SERVER_WORKERS environment variable to the desired number of compute nodes:

$ PHP_CLI_SERVER_WORKERS=8 php -S localhost:8080

This feature is still marked as experimental in PHP 8.1.


PHP has a built-in web server that provides a convenient way to test your applications and quickly expose local filesystem content on your local network. It supports PHP script execution, catch-all routing, and static files with most common MIME types.

Although the server now supports an optional fork mode, it is not recommended to use it in production. It’s designed as a development aid and lacks the customization and security features you’ll need for your public deployments. Where it excels is as a lightweight, integrated alternative to conventional development platforms such as WAMP, XAMPP, and Docker containers.

Comments are closed.