13.x, and framework references point to the latest release, v13.34.0.
loadRoutesFrom only loads a file
ServiceProvider::loadRoutesFrom() does not load the route file when the application implements CachesRoutes and routesAreCached() returns true. Otherwise, it requires the specified file.
The method itself does not add URI or route name prefixes, a controller namespace, or middleware. It also does not publish files or add routes to an existing cache.
The diagram assumes a standard Laravel application. There is no package-specific cache. Instead, the package routes are included in the application-wide route cache.
Keep configuration and registration separate
The following example creates a public endpoint that reports whether the package is available. It assumes that Composer’s PSR-4 configuration mapsAcme\Courier\ to src/ and that the provider is registered through auto-discovery or manually.
config/courier.php
register() and load routes in boot(). Do not make a provider that registers HTTP routes a DeferrableProvider, because there is no longer any guarantee that the provider boots by the time the routes are needed.
src/CourierServiceProvider.php
routes/web.php
src/Http/Controllers/StatusController.php
/acme-courier/status, and the route name is acme-courier.status. If you generate URLs with route('acme-courier.status'), callers can keep using the same route name even when the URI prefix changes. The group’s name() concatenates strings as is, so include the trailing . as well.
web is not a substitute for authentication or authorization. This example is a public endpoint that contains no sensitive information. For endpoints that return user data, add authentication middleware and authorization logic that match your requirements.Prevent URI and route name collisions separately
URI prefixes and route name prefixes are separate mechanisms. Adding one does not prevent collisions in the other.
When
AbstractRouteCollection builds a route collection for caching, it throws a LogicException if another route already has the same name. Being able to generate URLs during a normal boot does not guarantee that the routes can be cached. Two routes with different URIs still cause a problem if they share a name.
Do not rely on overriding the consuming application’s routes through registration order as a way to extend your package. If needed, provide a setting that disables the routes and a service that users can call from their own routes.
Configuration at cache time is baked into route definitions
RouteCacheCommand first runs route:clear, then boots a fresh application and collects its routes. It prepares those routes in a serializable form and writes the compiled result to the cache file.
The package route file is also loaded at this point, so the prefix and whether the routes are registered are determined by the configuration at the time the cache is built. On subsequent boots, loadRoutesFrom() does not load the file, and the cached routes are used.
Do not register routes based on conditions that change per request, such as the user or tenant. Those conditions are evaluated in the CLI environment when the cache is built. Register routes with a stable configuration, and decide whether access is allowed through middleware or authorization in the controller.
Finalize configuration first during deployment
After updating code and configuration, rebuild in the following order if your setup uses the configuration cache. Include these steps in the consuming application’s deployment process.route:cache while a stale configuration cache remains, the routes are also built with the old configuration. Rerunning only config:cache does not update the route cache. With -vv, you can also inspect the contents of middleware groups.
To verify behavior without caches during development, clear both as needed.
Combinations to verify before release
In addition to the package tests, verify the following combinations in a Laravel 13 consuming application. Cover not only in-memory route registration but also the path where Artisan boots a fresh application.- Without a cache,
/acme-courier/statusresponds, and the route name and middleware are as expected. route:cachesucceeds, and a fresh boot responds with the same URI and route name.- After changing the prefix and rebuilding the cache, the new URI responds and the package route at the old URI is gone.
- After disabling the routes and rebuilding the cache, the target route does not appear in
route:list --name=acme-courier. - URIs and route names do not collide with the consuming application or other packages.
Related pages
Routing
Review the basics of route groups, named routes, and listing routes.
Package config merging and caching
Review update procedures that account for published configuration and the configuration cache.
Deferred Service Providers
Learn why providers that register routes should not be deferred.
Package Version Compatibility Management
Connect public API changes to your release policy and continuous verification.
Primary sources
- Laravel official documentation: Package routes
- Laravel official documentation: Routing
- ServiceProvider: Implementation of loadRoutesFrom
- RouteServiceProvider: Loading the cache
- RouteCacheCommand: Collecting and saving routes in a fresh application
- AbstractRouteCollection: Detecting duplicate route names