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

Basepath Laravel Package

selective/basepath

Selective Basepath lets Laravel routes use a different base path per request, so apps deployed in a subdirectory can still serve selected routes from the domain root. Middleware-driven configuration keeps URL generation, redirects, and assets correct without duplicating routes or rewriting config.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require selective/basepath

The package is two small classes. For a Laravel codebase, only one of them matters:

  • Selective\BasePath\BasePathDetector — the useful part. Give it a $_SERVER-shaped array and it returns the base path (/intranet, or '' when at the web root).
  • Selective\BasePath\BasePathMiddleware — a PSR-15 middleware whose constructor expects a Slim App. It is not wiring you want in Laravel; skip it and read it only as a reference implementation for "detect, then apply to the app".

So the first thing to open is src/BasePathDetector.php and its unit tests — the tests are the real specification of the heuristics, because they enumerate the exact SCRIPT_NAME / REQUEST_URI / PHP_SELF / ORIG_PATH_INFO combinations the detector handles.

First use case: an app served from a subdirectory behind nginx or an ALB path-prefix, where url() and asset() come out missing the prefix (or with a doubled one) and you cannot get TrustProxies to agree with what the web server reports.

use Selective\BasePath\BasePathDetector;

$detector = new BasePathDetector($_SERVER);
$basePath = $detector->getBasePath(); // '/intranet' or ''

Bind it as a request-scoped value rather than a naive singleton, because $_SERVER differs between HTTP and CLI:

// app/Providers/AppServiceProvider.php
use Illuminate\Http\Request;
use Selective\BasePath\BasePathDetector;

$this->app->bind(BasePathDetector::class, function ($app) {
    $config = $app['config']->get('app.base_path');

    // Explicit config always wins over detection.
    return new BasePathDetector($app['request']->server->all(), $config ?: null);
});

Then consume it in a middleware that forces the URL root:

public function handle(Request $request, Closure $next)
{
    $basePath = app(BasePathDetector::class)->getBasePath();

    if ($basePath !== '' && $basePath !== '/') {
        URL::forceRootUrl(rtrim(config('app.url'), '/') . $basePath);
    }

    return $next($request);
}

Implementation Patterns

Config-first, detection-second. Treat the detector as a fallback, not the source of truth. Add APP_BASE_PATH to config/app.php; only let BasePathDetector run when it is empty. Subdirectory deployments are exactly the case where detection misbehaves, and a wrong-but-silent base path is worse than a required env var.

Portable base-path service. The genuine value in a Laravel shop is when you are migrating a legacy Slim 4 app one route at a time and both apps must agree on their mount point. Wrap the detector in a small BasePathResolver used by both bootstraps, so /legacy resolves identically in Slim's $app->setBasePath() and in Laravel's URL::forceRootUrl():

final class BasePathResolver
{
    public function resolve(array $server, ?string $configured = null): string
    {
        return rtrim((new BasePathDetector($server, $configured))->getBasePath(), '/');
    }
}

Mounting a Slim app inside Laravel. If you must run Slim behind a Laravel catch-all route (a real, if unpleasant, migration pattern), you are on the path the package is built for: new BasePathMiddleware($slimApp) inside the Slim bootstrap, with Slim instantiated per request in a Laravel controller. Do not share $_SERVER mutations between the two — pass the request's server bag into Slim explicitly.

Apply at one chokepoint. Do not call getBasePath() in Blade, in a helper, and in a mail template. Detect once per request in middleware, store it on the container or a request attribute, and let URL/asset() inherit it. Multiple call sites are how you end up with /intranet/intranet/login.

CLI and queue safety. Commands, jobs, and php artisan serve have no meaningful SCRIPT_NAME. In those contexts the middleware never runs (which is correct); but if you inject BasePathDetector into a job constructor and the binding touches $app['request'], you will get an empty or stale request. Inject the resolved string, not the detector.

Test with fixtures, not with a real server. The detector is pure input/output — snapshot getBasePath() against arrays copied from your nginx fastcgi_param block. That is a five-minute test that catches the deployment configuration you actually have.

Gotchas and Tips

  • Laravel already knows its base path. $request->getBasePath() (Symfony) and $request->root() cover the standard cases, and URL::forceRootUrl() is the idiomatic override. Reach for this package only when those disagree with reality — typically path-prefix proxies. Be suspicious of adding a second, less-maintained mechanism to solve something the framework already does.
  • No X-Forwarded-Prefix support. The detector reads SCRIPT_NAME, REQUEST_URI, PHP_SELF, and ORIG_PATH_INFO; it does not understand the X-Forwarded-Prefix header that many ingress controllers use for stripping. If your proxy strips rather than rewrites, you will need a custom decoration in front of the detector, or configure the ingress to rewrite instead. This is the single most common reason a detection comes back empty.
  • PHP built-in server returns ''. php -S localhost:8000 -t public has no meaningful script name. Expect empty detection locally and a real prefix in production — the worst possible difference for a "detect automatically" feature.
  • '' is not '/'. Guard against rtrim producing '', then concatenating, then getting //login. Normalize in one place and unit test it.
  • Trailing slashes and encoding. The detector returns the prefix without a trailing slash, but URL-encoded segments in REQUEST_URI are passed through — if your mount point contains spaces or non-ASCII, verify the result rather than trusting it.
  • Explicit constructor arg wins. The second parameter (?string $basePath) short-circuits detection entirely. That is your escape hatch whenever detection is wrong in production, and it should be wired to a config value on day one.
  • Bind per request, not as a singleton. Anything cached at container-build time in a long-running worker (Octane, Horizon) will leak the first request's server variables into subsequent ones.
  • BasePathMiddleware is Slim-locked. It requires the Slim App in its constructor. Do not try to shoehorn it into Laravel's middleware stack — the signature does not fit. Copy the three lines of logic instead.
  • Low ecosystem surface is a feature here. Zero dependents and ~48 stars mean you are on your own for fixes, but the package is two files with an MIT license: vendoring or forking it to add X-Forwarded-Prefix handling is more sensible than fighting it. Check the CHANGELOG before upgrading — the 2026-02-05 release landed after a long gap, so pin the version and read the diff.
  • Debugging move: when detection looks wrong, dump $_SERVER['SCRIPT_NAME'], $_SERVER['REQUEST_URI'], and $_SERVER['PHP_SELF'] together. The detector's output is always a function of those three, and comparing them side by side explains the result in seconds.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib