hit counter

How to test websites quickly with PHP’s built-in web server

Image shows the PHP logo

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

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

Using the integrated server

The built-in server is a handy mechanism to help you test PHP sites in environments that don’t have 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 warned that it is designed for development purposes only. The PHP documentation specifically warns against using this server before 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 too 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 passed to the command specifies the receiving address of the server. We used port 8080 on localhost in the example above. Now you can visit http://localhost:8080 in your web browser to access the content in your working directory. All PHP scripts run automatically when you request them.

You can provide a path that is outside of your working directory by using the -t Flag when starting the server:

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

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

Keep your terminal window open while using the web server. Press Ctrl+C to cancel the process once you’re done testing your site. PHP logs every incoming request in your terminal window, including the URI and HTTP method. Any uncaught PHP errors are also shown in the logs.

Enable remote access

listen localhost does not allow incoming connections from other devices on your network. You can allow remote access by binding to 0.0.0.0 instead of this:

$ php -S 0.0.0.0:8080

Keep in mind that the server is not hardened for production use and should not be made publicly available. Allow remote access only when absolutely necessary, e.g. B. testing a specific feature on a mobile device. Make sure the port you are using is not open to the internet.

Request fallback matching

PHP will search index.php and index.html Files in the root of the active document when the incoming request is missing a URI component. If none of these files exist, the server moves further up the directory tree, looking for an index in one of the parent documents of your document root. This means you can unintentionally provide content that is outside of the directory you specify. A 404 Not Found status is returned when the top of the tree is reached without an index file being found.

Requests that contain a URI (ex /file) must exactly match a static file in the document root. Otherwise a 404 is returned. PHP automatically sets the Content-Type Response headers to the MIME type of the provided file for most common file extensions.

Using a router script

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

Router functionality is enabled by specifying a PHP filename on the command line when starting the server:

$ php -S localhost:8080 router.php

PHP is now in use router.php to handle everyone incoming request. You can route users to the appropriate place in your application by checking the request URI:

if ($_SERVER["REQUEST_URI"] === "/dashboard") {
    require_once("dashboard.php");
}
else if ($_SERVER["REQUEST_URI"] === "/profile") {
    require_once("profile.php");
}
else {
    require_once("404.php");
}

The output produced by your router script becomes the response sent back to the client. An exception is when the script returns false: In this case, PHP falls back to returning 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;
}

Detecting the built-in server from your PHP code

Your PHP code can tell if it’s being called from the built-in web server by examining the name of the active interface. That php_sapi_name() function returns this value. It will be discontinued cli-server if the script was called from the built-in server component.

if (php_sapi_name() === "cli-server") {
    enable_development_mode();
}

Simultaneous processing of multiple requests

By default, the server runs in a synchronous single-process mode. Requests are handled individually and block each other from execution until they complete. This is one of the reasons why the server is unsuitable for productive use.

PHP 7.4 added support for processing multiple requests at the same time. It relies on fork() Availability and does not work on Windows. The server forks a new worker to service each incoming request when this mode is enabled. You can enable it by setting that PHP_CLI_SERVER_WORKERS Environment variable to the number of desired workers:

$ PHP_CLI_SERVER_WORKERS=8 php -S localhost:8080

This functionality is still marked as experimental in PHP 8.1.

summary

PHP has a built-in web server that allows you to conveniently test your applications and quickly expose local file system content on your local network. It supports PHP script execution, catch-all routing and static files with the most popular MIME types.

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

Leave a Comment