Laravel Valet is a great tool for quickly spinning up local development environments on macOS. However, if you’ve ever tried to run a WordPress multisite installation using subdirectories (like /de/category/post-name
) under Valet, you might have noticed something’s not quite right.
In this post, I’ll show you how I fixed this routing issue by creating a custom Valet driver specifically for WordPress multisite subdirectory setups.
🧩 The Problem
Valet uses “drivers” to determine how to serve requests for different types of projects (Laravel, WordPress, etc.). The default WordPress driver works fine for single-site installs or subdomain multisites, but not for subdirectory-based multisites.
For example, visiting:
http://wordpress-site.com/de/category/post-slug/
…incorrectly routes you to the home page instead of resolving the correct category or post.
🔧 The Solution: Custom Valet Driver
To solve this, I created a custom Valet driver that handles WordPress multisites using subdirectory installations. Here’s the custom driver:
// ~/.config/valet/Drivers/WordPressMultisiteSubdirectoryValetDriver.php
namespace Valet\Drivers\Custom;
use Valet\Drivers\BasicValetDriver;
class WordPressMultisiteSubdirectoryValetDriver extends BasicValetDriver
{
public $wp_root = false;
public function serves(string $sitePath, string $siteName, string $uri): bool
{
return file_exists($sitePath . '/wp-config.php') &&
strpos(file_get_contents($sitePath . '/wp-config.php'), 'MULTISITE') !== false &&
(
strpos(file_get_contents($sitePath . '/wp-config.php'), "define('SUBDOMAIN_INSTALL',false)") ||
strpos(file_get_contents($sitePath . '/wp-config.php'), "define('SUBDOMAIN_INSTALL', false)") ||
strpos(file_get_contents($sitePath . '/wp-config.php'), "define( 'SUBDOMAIN_INSTALL', false )")
);
}
public function frontControllerPath(string $sitePath, string $siteName, string $uri): string
{
$_SERVER['PHP_SELF'] = $uri;
$_SERVER['SERVER_ADDR'] = '127.0.0.1';
$_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'];
if (stripos($uri, 'wp-admin') !== false || stripos($uri, 'wp-content') !== false || stripos($uri, 'wp-includes') !== false) {
if (stripos($uri, 'wp-admin/network') === false) {
$uri = substr($uri, stripos($uri, '/wp-'));
}
if ($this->wp_root !== false && file_exists($sitePath . "/{$this->wp_root}/wp-admin")) {
$uri = "/{$this->wp_root}" . $uri;
}
}
if (stripos($uri, 'wp-cron.php') !== false) {
$new_uri = substr($uri, stripos($uri, '/wp-'));
if (file_exists($sitePath . $new_uri)) {
return $sitePath . $new_uri;
}
}
return parent::frontControllerPath($sitePath, $siteName, $this->forceTrailingSlash($uri));
}
public function isStaticFile(string $sitePath, string $siteName, string $uri)
{
if (stripos($uri, 'wp-admin') !== false || stripos($uri, 'wp-content') !== false || stripos($uri, 'wp-includes') !== false) {
if (substr($uri, -1, 1) == "/") return false;
$new_uri = substr($uri, stripos($uri, '/wp-'));
if ($this->wp_root !== false && file_exists($sitePath . "/{$this->wp_root}/wp-admin")) {
$new_uri = "/{$this->wp_root}" . $new_uri;
}
if (file_exists($sitePath . $new_uri)) {
return $sitePath . $new_uri;
}
}
return parent::isStaticFile($sitePath, $siteName, $uri);
}
private function forceTrailingSlash(string $uri): string
{
if (substr($uri, -9) == '/wp-admin') {
header('Location: '.$uri.'/'); die;
}
return $uri;
}
}
How to Use It
- Create a custom driver file at:
~/.config/valet/Drivers/WordPressMultisiteSubdirectoryValetDriver.php
- Paste the code above into the file.
- Run:
valet restart
- Visit your multisite subdirectory URLs like
/de/category/...
— they should now work as expected!
If you’re working with WordPress multisite on Laravel Valet using subdirectories, this custom driver is a simple and effective fix. It’s lightweight, and integrates seamlessly with how Valet expects drivers to behave.