Sitemap

Running OroCommerce with PHP -s Built-in Server

3 min readMay 4, 2026

--

Guide of running OroCommerce without the overhead of a traditional web server stack. Using the built-in PHP server is a “lean” developer’s dream, provided you handle the routing correctly.

Below is your article, polished for readability with enhanced formatting and clear visual separation for the technical steps.

How to start OroCommerce locally without Nginx, Apache, or the Symfony CLI — just php -S. This approach is ideal for local development, E2E testing, and CI environments where a full web server is unnecessary overhead.

The One-Line Mistake Everyone Makes

If you use the standard PHP server command pointing directly to index.php, every .js, .css, and image request will be treated as a PHP script. Because OroCommerce's compiled bundles are complex, PHP tries to execute them and fails.

# WRONG — every .js/.css request goes through PHP
php -S 0.0.0.0:8000 -t public/ public/index.php

# RIGHT - static files served directly, PHP only for app routes
php -S 0.0.0.0:8000 -t public/ router.php

Why it breaks

When using the wrong router, you’ll see errors like:

Fatal error: Uncaught TypeError: Invalid return value... from "/public/build/app.js"

The result is a “Ghost Load”: the HTML structure appears, but the Oro AMD/RequireJS module system never boots, leaving you stuck on a “Loading…” screen forever.

How router.php Works

OroCommerce ships with a router.php in the project root. This script acts as a traffic cop:

<?php
$requestUri = $_SERVER['REQUEST_URI'];
$path = parse_url($requestUri, PHP_URL_PATH);
$file = __DIR__ . '/public' . $path;

// 1. If the file exists on disk (css, js, images), serve it as-is.
if ($path !== '/' && file_exists($file) && !is_dir($file)) {
return false;
}
// 2. Otherwise, forward the request to Symfony's front controller.
require __DIR__ . '/public/index.php';

return false tells the built-in server: "I'm not handling this; serve the physical file directly."

1. Configure the EnviroVerifying Health

Use these quick terminal commands to ensure everything is routed correctly:

  • Smoke Test: curl -I http://localhost:8000/ (Should return 200 OK)
  • Static Asset Test: curl -I http://localhost:8000/build//app.js
  • Success: 200 OK
  • Failure: 500 Internal Server Error (You are likely using index.php as the router instead of router.php).

Requred PHP Extensions

OroCommerce is demanding. Ensure you have the following installed:

bcmath, curl, gd, intl, mbstring, opcache, pdo_pgsql, soap, xml, zip.

Performance Optimization

The built-in server is single-threaded. To make it snappier for local dev, create a php-local.ini and increase the memory limit and realpath cache:

memory_limit = 2048M
realpath_cache_size = 4096K
realpath_cache_ttl = 600
opcache.enable = 1

Start the server with this config:

php -c php-local.ini -S 0.0.0.0:8000 -t public/ router.php

Fix single threaded server:

You can tell the PHP server to spawn multiple “worker” processes. This allows the server to handle multiple concurrent requests (e.g., loading the HTML while simultaneously fetching JS and CSS assets).

Command:

# Set 4 workers and start the server
PHP_CLI_SERVER_WORKERS=4 php -S 0.0.0.0:8000 -t public/ router.php

Why skip the Symfony CLI?

While symfony serve is excellent, it adds an 3d party external dependency to your toolchain. Using php -S with router.php relies strictly on what is already in the project repository and your PHP installation, making it more portable for CI/CD pipelines and simplified onboarding.

--

--

Yegor Shytikov
Yegor Shytikov

Written by Yegor Shytikov

True Stories about Magento 2. Melting down metal server infrastructure into cloud solutions.