内置 Web Server

警告

该 Web 服务器目的是帮助应用程序开发。还可用于测试目的或在受控环境中运行的应用程序演示。但并不是一个功能齐全的 Web 服务器。不应该在公共网络上使用。

CLI SAPI 提供了内置的 Web 服务器。

Web 服务器仅运行一个单线程进程,因此如果堵塞了请求,PHP 应用程序将停止运行。

URI 请求将从启动 PHP 的当前工作目录提供服务,除非使用 -t 选项指定文档根目录。如果 URI 请求未指定文件,则返回指定目录中的 index.php 或 index.html。如果两个文件都不存在,则将在父目录中继续查找 index.php 和 index.html,依此类推,直到找到一个文件或到达文档根目录。如果找到 index.php 或 index.html,则将其返回,并将 $_SERVER['PATH_INFO'] 设置为 URI 的尾部部分。否则将返回 404 响应代码。

如果在命令行上启动 Web 服务器时指定了 PHP 文件,则会将其视为“router”脚本。该脚本在每个 HTTP 请求开始时运行。如果此脚本返回 false,则按原样返回所请求的资源。否则脚本的输出将返回到浏览器。

对于有扩展名的文件,将返回标准的 MIME 类型: .3gp.apk.avi.bmp.css.csv.doc.docx.flac.gif.gz.gzip.htm.html.ics.jpe.jpeg.jpg.js.kml.kmz.m4a.mov.mp3.mp4.mpeg.mpg.odp.ods.odt.oga.ogg.ogv.pdf.png.pps.pptx.qt.svg.swf.tar.text.tif.txt.wav.webm.wmv.xls.xlsx.xml.xsl.xsd.zip

从 PHP 7.4.0 开始,内置 Web 服务器可以配置为派生多个工作线程,以便测试需要向内置 Web 服务器发出多个并发请求的代码。在启动服务器之前,将 PHP_CLI_SERVER_WORKERS 环境变量设置为所需的 worker 数量。

注意: 此功能不支持 Windows。

警告

实验功能适用于生产用途。通常,内置 Web 服务器适合生产用途。

示例 #1 启动 Web 服务器

$ cd ~/public_html
$ php -S localhost:8000

终端将显示:

PHP 5.4.0 Development Server started at Thu Jul 21 10:43:28 2011
Listening on localhost:8000
Document root is /home/me/public_html
Press Ctrl-C to quit

在 URI 请求 http://localhost:8000/ 和 http://localhost:8000/myscript.html 之后,终端会显示类似以下的内容:

PHP 5.4.0 Development Server started at Thu Jul 21 10:43:28 2011
Listening on localhost:8000
Document root is /home/me/public_html
Press Ctrl-C to quit.
[Thu Jul 21 10:48:48 2011] ::1:39144 GET /favicon.ico - Request read
[Thu Jul 21 10:48:50 2011] ::1:39146 GET / - Request read
[Thu Jul 21 10:48:50 2011] ::1:39147 GET /favicon.ico - Request read
[Thu Jul 21 10:48:52 2011] ::1:39148 GET /myscript.html - Request read
[Thu Jul 21 10:48:52 2011] ::1:39149 GET /favicon.ico - Request read

请注意,在 PHP 7.4.0 之前,在 Windows 上无法访问符号链接的静态资源,除非 router 脚本可以处理这些资源。

示例 #2 启动时指定文档根目录

$ cd ~/public_html
$ php -S localhost:8000 -t foo/

终端将显示:

PHP 5.4.0 Development Server started at Thu Jul 21 10:50:26 2011
Listening on localhost:8000
Document root is /home/me/public_html/foo
Press Ctrl-C to quit

示例 #3 使用 router 脚本

在此示例中,请求图像将显示图片,但对 HTML 文件的请求将显示“Welcome to PHP”:

<?php
// router.php
if (preg_match('/\.(?:png|jpg|jpeg|gif)$/', $_SERVER["REQUEST_URI"])) {
return
false; // 原样提供所请求的资源
} else {
echo
"<p>Welcome to PHP</p>";
}
?>
$ php -S localhost:8000 router.php

示例 #4 检查 CLI Web 服务器的使用

要在使用 CLI Web 服务器开发期间以及稍后使用生产 Web 服务器重复使用的框架 router 脚本:

<?php
// router.php
if (php_sapi_name() == 'cli-server') {
/* 发送静态资源并返回 false */
}
/* 继续正常的 index.php 操作 */
?>
$ php -S localhost:8000 router.php

示例 #5 处理不支持的文件类型

如果需要提供不由 CLI Web 服务器处理的静态资源的 MIME 类型,请使用:

<?php
// router.php
$path = pathinfo($_SERVER["SCRIPT_FILENAME"]);
if (
$path["extension"] == "el") {
header("Content-Type: text/x-script.elisp");
readfile($_SERVER["SCRIPT_FILENAME"]);
}
else {
return
FALSE;
}
?>
$ php -S localhost:8000 router.php

示例 #6 从远程计算机访问 CLI Web 服务器

可以通过以下方式使 Web 服务器可通过端口 8000 接受任何链接:

$ php -S 0.0.0.0:8000
警告

内置 Web 服务器不应该在公共网络上使用。

add a note

User Contributed Notes 16 notes

up
120
jonathan at reinink dot ca
10 years ago
In order to set project specific configuration options, simply add a php.ini file to your project, and then run the built-in server with this flag:

php -S localhost:8000 -c php.ini

This is especially helpful for settings that cannot be set at runtime (ini_set()).
up
65
Mark Simon
7 years ago
It’s not mentioned directly, and may not be obvious, but you can also use this to create a virtual host. This, of course, requires the help of your hosts file.

Here are the steps:

1    /etc/hosts
    127.0.0.1    www.example.com

2    cd [root folder]
    php -S www.example.com:8000

3    Browser:
    http://www.example.com:8000/index.php

Combined with a simple SQLite database, you have a very handy testing environment.
up
56
oan at vizrt dot com
7 years ago
I painfully experienced behaviour that I can't seem to find documented here so I wanted to save everyone from repeating my mistake by giving the following heads up:

When starting php -S on a mac (in my case macOS Sierra) to host a local server, I had trouble with connecting from legacy Java.

As it turned out, if you started the php server with
"php -S localhost:80"
the server will be started with ipv6 support only!

To access it via ipv4, you need to change the start up command like so:
"php -S 127.0.0.1:80"
which starts server in ipv4 mode only.
up
28
tamas at bartatamas dot hu
9 years ago
If your URI contains a dot, you'll lose the $_SERVER['PATH_INFO'] variable, when using the built-in webserver.
I wanted to write an API, and use .json ending in the URI-s, but then the framework's routing mechanism broke, and it took a lot of time to discover that the reason behind it was its router relying on $_SERVER['PATH_INFO'].

References:
https://bugs.php.net/bug.php?id=61286
up
22
matthes at leuffen dot de
7 years ago
To output debugging information on the command line you can write output to php://stdout:

<?php
$path
= $_SERVER["SCRIPT_FILENAME"];

file_put_contents("php://stdout", "\nRequested: $path");
echo
"<p>Hello World</p>";
?>
up
27
Ivan Ferrer
10 years ago
On Windows you may find useful to have a phpserver.bat file in shell:sendto with the folowing:
explorer http://localhost:8888
rem check if arg is file or dir
if exist "%~1\" (
  php -S localhost:8888 -t "%~1"
) else (
  php -S localhost:8888 -t "%~dp1"
)

then for fast web testing you only have to SendTo a file or folder to this bat and it will open your explorer and run the server.
up
3
deep at deepshah dot me
3 years ago
Listen on all addresses of IPv4:
php -S 0.0.0.0:80

Listen on all addresses of IPv6:
php -S [::0]:80
up
3
dachund at gmail dot com
6 years ago
I fiddled around with the internal webserver and had issues regarding handling static files, that do not contain a dot and a file extension.

The webserver responded with 200 without any content for files with URIs like "/testfile".

I am not certain if this is a bug, but I created a router.php that now does not use the "return false;" operation in order to pass thru the static file by the internal webserver.

Instead I use fpassthru() to do that.

In addition to that, my router.php can be configured to...
- ... have certain index files, when requesting a directory
- ... configure regex routes, so that, if the REQUEST_URI matches the regex, a certain file or directory is requested instead. (something you would do with nginx config or .htaccess ModRewrite)

Maybe someone finds this helpful.

================================

<?php

$indexFiles
= ['index.html', 'index.php'];
$routes = [
 
'^/api(/.*)?$' => '/index.php'
];

$requestedAbsoluteFile = dirname(__FILE__) . $_SERVER['REQUEST_URI'];

// check if the the request matches one of the defined routes
foreach ($routes as $regex => $fn)
{
  if (
preg_match('%'.$regex.'%', $_SERVER['REQUEST_URI']))
  {
   
$requestedAbsoluteFile = dirname(__FILE__) . $fn;
    break;
  }
}

// if request is a directory call check if index files exist
if (is_dir($requestedAbsoluteFile))
{
  foreach (
$indexFiles as $filename)
  {
   
$fn = $requestedAbsoluteFile.'/'.$filename;
    if (
is_file($fn))
    {
     
$requestedAbsoluteFile = $fn;
      break;
    }
  }
}

// if requested file does not exist or is directory => 404
if (!is_file($requestedAbsoluteFile))
{
 
header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
 
printf('"%s" does not exist', $_SERVER['REQUEST_URI']);
  return
true;
}

// if requested file is'nt a php file
if (!preg_match('/\.php$/', $requestedAbsoluteFile)) {
 
header('Content-Type: '.mime_content_type($requestedAbsoluteFile));
 
$fh = fopen($requestedAbsoluteFile, 'r');
 
fpassthru($fh);
 
fclose($fh);
  return
true;
}

// if requested file is php, include it
include_once $requestedAbsoluteFile;
up
0
devoldemar
4 months ago
Built-in web server uses SAPI logging subsystem. Therefore all messages are written to standard error, and not to standard output stream.
If you want to save server logs into a file, the following command will work:
php -S 0.0.0.0:80 2>&1 | tee out.log
up
1
sony at sony-ak dot com
4 years ago
To send environment variable as long as with PHP built-in web server, type like this.

~$ MYENV=dev php -d variables_order=EGPCS -S 0.0.0.0:8000

On PHP script we can check with this code.

<?php
 
echo getenv('MYENV'); // print dev
up
-2
dwingardjr at gmail dot com
6 years ago
Just a note to people who also use windows 8.1, or anyone who has had this problem when running the using the PHP server CLI.

`PHP -S localhost:8000 -t /public` <-- Not going to work.

`PHP -S localhost:8000 -t public` <-- Works!

And there is something else up in the notes saying something about you can't serve a project folder and a router file. Well, actually you can! At least for me.

`PHP -S localhost:8000 router.php -t public` <-- Perhaps someone tries this and it doesn't work.

`PHP -S localhost:8000 -t public router.php` <-- Works!
up
-19
gyunaev at gmail dot com
6 years ago
You can also print messages to the server's STDOUT via error_log().

Also the documentation doesn't make it clear that when you use router script if a PHP file is requested and you return false, the PHP file will be served (i.e. you do not need to load and eval it manually).
up
-17
Lukas
4 years ago
For serving static content like .css or .js and otherwise using a router (for me it was index.php)  this worked out of the box for me:

   php -S localhost:8000

Due to my router file was index.php. But

   php -S localhost:8000 index.php

did not work, because my static files are not served via my router.
up
-31
ohcc at 163 dot com
7 years ago
$_SERVER['SERVER_ADDR'] is not defined when using php as the built-in commandline web server, so you can not use $_SERVER['SERVER_ADDR'] to detect the Server's IP address.

P.S.: This is tested on Windows with PHP 7.1 on 2016-12-22.

Below is the printed $_SERVER variable.

Array
(
    [DOCUMENT_ROOT] => E:\Programs\PHPServer\www\srv
    [REMOTE_ADDR] => 118.117.61.32
    [REMOTE_PORT] => 10865
    [SERVER_SOFTWARE] => PHP 7.1.0 Development Server
    [SERVER_PROTOCOL] => HTTP/1.1
    [SERVER_NAME] => 0.0.0.0
    [SERVER_PORT] => 8080
    [REQUEST_URI] => /
    [REQUEST_METHOD] => GET
    [SCRIPT_NAME] => /index.php
    [SCRIPT_FILENAME] => E:\Programs\PHPServer\www\srv\index.php
    [PHP_SELF] => /index.php
    [HTTP_HOST] => www.wuxiancheng.cn:8080
    [HTTP_CONNECTION] => keep-alive
    [HTTP_CACHE_CONTROL] => max-age=0
    [HTTP_UPGRADE_INSECURE_REQUESTS] => 1
    [HTTP_USER_AGENT] => Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
    [HTTP_ACCEPT] => text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    [HTTP_DNT] => 1
    [HTTP_ACCEPT_ENCODING] => gzip, deflate, sdch
    [HTTP_ACCEPT_LANGUAGE] => zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4
    [HTTP_COOKIE] => qbbs_2132_saltkey=fZ7509n5; qbbs_2132_lastvisit=1482156014; Hm_lvt_f812a4362ef73c80c4d13485d1ab3a49=1482159614; _ga=GA1.2.1594404236.1482159615; su=727vL6EEPLqjcyfJcad-za9eVYOh1i7e; Hm_lvt_6a65b0f2004e441e86ecea9c3562d997=1482232509,1482241896,1482242293,1482296586
    [REQUEST_TIME_FLOAT] => 1482390410.65625
    [REQUEST_TIME] => 1482390410
)
up
-20
Anonymous
3 years ago
If you have trouble with a project using both dynamic routes containing dots (giving unexpected 404 errors) and static file hosting paste this in your index.php

// Support cli server for local development
if (php_sapi_name() === 'cli-server') {
    $fileName = __DIR__.parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
    if (file_exists($fileName) && !is_dir($fileName)) return false;
}

Then run the internal server directly on the file:

php -S 127.0.0.1 index.php
up
-48
eyecatchup at gmail dot com
6 years ago
Note: The built-in web server has a file size limit. For files larger than 5 GB, it will always serve a "File not found" error page.
To Top