Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Server Command Laravel Package

wp-cli/server-command

WP-CLI Server Command adds the wp server command, letting you launch PHP's built-in web server to run WordPress locally without Apache or Nginx. Just run wp server and get a working dev site, with options for host, port, docroot, and router. Ideal for quick local development and testing.

Deep Wiki
Context7

Getting Started

wp-cli/server-command provides the wp server command. It launches PHP's built-in web server (php -S) against a WordPress document root and ships with a router that makes pretty permalinks work. It is a local-development tool, not a production web server.

Minimal setup:

# Confirm WP-CLI and the command are available
wp --info
wp help server

# Run from the WordPress root (the directory containing index.php and wp-config.php)
cd /path/to/wordpress
wp server

Then open http://localhost:8080. Stop the server with Ctrl+C.

If wp server is not recognized:

wp package install wp-cli/server-command
# or update WP-CLI, since the command is commonly bundled
wp cli update

Where to look first:

  • wp help server for the exact options supported by your installed version.
  • The package's bundled router.php to understand how static files and WordPress permalinks are handled.
  • The terminal output while the server runs: PHP's built-in server logs requests and PHP errors there.

First real use case: skip MAMP/Local/Valet for a quick plugin or theme check. From a checked-out WordPress site, run wp server, activate your plugin with wp plugin activate my-plugin, and iterate in the browser. Because it runs before WordPress is fully bootstrapped, startup is fast, but the HTTP requests themselves still load WordPress normally.

Implementation Patterns

Basic daily loop

wp server --host=127.0.0.1 --port=8080

Useful options:

Option Purpose
--host=<host> Bind address. Default localhost. Use 0.0.0.0 for containers or LAN/device testing.
--port=<port> Listen port. Default 8080.
--docroot=<path> Directory to serve. Default is the current working directory.
--router=<file> Custom router script. Default is the package's bundled router.

Run from a subdirectory or Bedrock-style layout

--docroot must point at the directory containing index.php, not necessarily the project root:

# WordPress installed under public/
wp server --docroot=public

# Bedrock-style layout
wp server --docroot=web

If WP-CLI itself cannot locate WordPress from your current directory, combine the global --path flag with the server's --docroot:

wp --path=public server --docroot=public

--path tells WP-CLI where WordPress lives; --docroot tells the web server what to serve. They are separate concerns.

Keep local URLs in sync

The most common source of "why is it redirecting to production?" is stale home and siteurl values:

wp db export backup.sql
wp option update home 'http://localhost:8080'
wp option update siteurl 'http://localhost:8080'

# Or migrate all URLs, including serialized data
wp search-replace 'https://example.com' 'http://localhost:8080' --all-tables --precise

For per-developer setups, define constants in wp-config.php instead:

define( 'WP_HOME', 'http://localhost:8080' );
define( 'WP_SITEURL', 'http://localhost:8080' );

Also disable HTTPS-only admin behavior locally if it causes redirect loops:

define( 'FORCE_SSL_ADMIN', false );

Custom router for auth, CORS, or logging

The router is the main extension point. It runs on every request. Return false to let PHP's built-in server serve an existing file directly; otherwise include WordPress's front controller.

<?php
// router.php
$root = rtrim( $_SERVER['DOCUMENT_ROOT'], '/\\' );
$path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );

// Let php -S serve real static files.
if ( is_file( $root . $path ) ) {
    return false;
}

// Optional: basic auth for a shared dev environment.
if ( empty( $_SERVER['PHP_AUTH_USER'] ) ) {
    header( 'WWW-Authenticate: Basic realm="Dev"' );
    http_response_code( 401 );
    exit;
}

// Optional: request logging.
file_put_contents(
    sys_get_temp_dir() . '/wp-server.log',
    $_SERVER['REQUEST_METHOD'] . ' ' . $_SERVER['REQUEST_URI'] . PHP_EOL,
    FILE_APPEND
);

require $root . '/index.php';

Run it with:

wp server --router="$(pwd)/router.php"

Use an absolute path in scripts; relative router paths can be resolved from the current working directory.

Project scripts

Composer:

{
  "scripts": {
    "serve": "wp server --host=127.0.0.1 --port=8080"
  }
}
composer run serve

package.json:

{
  "scripts": {
    "wp:serve": "wp server --host=127.0.0.1 --port=8080"
  }
}

Per-project defaults with wp-cli.yml

WP-CLI supports command-specific defaults, which keeps the invocation short:

server:
  host: 127.0.0.1
  port: 8080
  docroot: web

Then wp server picks those defaults up in that project.

Docker and container workflows

Bind to all interfaces inside the container and publish the port:

wp server --host=0.0.0.0 --port=8080
services:
  wordpress:
    image: wordpress:cli
    command: wp server --host=0.0.0.0 --port=8080
    ports:
      - "8080:8080"

Verify the container user can write to wp-content/uploads; otherwise media uploads will fail even though the site loads.

Multisite

Subdirectory multisite generally works with the bundled router. Subdomain multisite needs hostnames such as site1.localhost:8080 to resolve and reach the server. Bind to 0.0.0.0, add host entries or wildcard DNS, and let WordPress route by HTTP_HOST.

Gotchas and Tips

  • Never expose wp server in production. PHP's built-in server has no TLS, no .htaccess/mod_rewrite, no process manager, and minimal concurrency control. Treat it as a disposable local server.

  • It is single-threaded by default. Any WordPress request that makes a loopback HTTP request to itself—WP Cron spawns, Site Health loopback tests, some REST integrations—can hang until timeout. On PHP 7.4+ (Unix-like systems), spawn workers:

    PHP_CLI_SERVER_WORKERS=4 wp server
    

    Also consider disabling WP Cron locally and running it manually:

    wp config set DISABLE_WP_CRON true --raw
    wp cron event run --due-now
    
  • No HTTPS. Browsers treat http://localhost as a secure context, so most development works, but OAuth providers and secure-cookie testing may still complain. Put a TLS-terminating reverse proxy or tunnel in front when needed.

  • Port conflicts are common. PHP reports Address already in use. Find the offender and switch ports:

    lsof -iTCP:8080 -sTCP:LISTEN
    wp server --port=8081
    

    If you change the port, update home/siteurl or your WP_HOME/WP_SITEURL constants to match.

  • localhost can bind IPv6 only on some systems. If the browser cannot connect but the process is running, try --host=127.0.0.1 explicitly.

  • --host=0.0.0.0 exposes the server to your network. There is no authentication by default. Use a custom router with basic auth or restrict access via firewall.

  • --path is not --docroot. Running wp server from a plugin directory usually serves the wrong directory and may not find WordPress. Run from the site root, or pass both flags.

  • The bundled router fixes pretty permalinks; a broken custom router can mask missing assets. If your router always includes index.php, requests for missing CSS/JS return HTML with a 200 instead of a 404. Return false only for real files, and let the server handle the rest.

  • return false only works in the top-level router script. If you factor router logic into an included file, the return value will not tell php -S to serve the file directly.

  • Code changes are picked up per request, but config changes are not. Editing PHP files does not require a restart. Changing php.ini, the PHP binary, or environment variables does. Restart with Ctrl+C and rerun.

  • Check which PHP binary is running. wp --info shows WP-CLI's PHP. If the server behaves differently from your web server, confirm extensions and versions. WP_CLI_PHP can override the binary WP-CLI uses.

  • File permissions still matter. The server runs as your shell user. If WordPress files are owned by www-data, uploads and plugin updates may fail. Fix ownership or run the server as the appropriate user.

  • Page-caching plugins can hide changes. Deactivate caching plugins during active development, or purge their caches after edits.

  • Redirect loops often come from stale URLs or forced SSL. Check home, siteurl, FORCE_SSL_ADMIN, and any WP_HOME/WP_SITEURL constants before debugging the server itself.

  • Log to a file when the terminal scrolls too fast.

    wp server 2>&1 | tee /tmp/wp-server.log
    
  • Debug WordPress errors through the terminal first. The built-in server prints PHP fatals and warnings to stderr. Also enable WP_DEBUG and WP_DEBUG_LOG for errors that occur after WordPress boots.

  • Custom routers are the best extension point. Use them for request logging, basic auth, CORS headers, SPA fallbacks, or special handling for /wp-json/. Do not use them as a substitute for production rewrite rules.

  • Windows support is limited. PHP_CLI_SERVER_WORKERS is not supported there, and path/quoting behavior can differ. Prefer WSL for parity with Unix-like development environments.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core