Laravel URL Generation Tutorial
Laravel provides several helpers and facades to generate URLs dynamically. Instead of hardcoding URLs in your application, you can use these helpers to create links that automatically adapt when your routes, parameters, or environment change.
This is essential for:
- Building links in Blade templates.
- Returning URLs in API responses.
- Creating redirects dynamically.
The Basics of URL Generation
Generating URLs with url()
The url() helper is the simplest way to generate a URL:
$post = App\Models\Post::find(1);
echo url("/posts/{$post->id}");
// Output: http://example.com/posts/1
🔹 Laravel automatically detects your current scheme (http or https) and host.
Adding Query Parameters
Use url()->query() to add query strings:
echo url()->query('/posts', ['search' => 'Laravel']);
// https://example.com/posts?search=Laravel
echo url()->query('/posts?sort=latest', ['search' => 'Laravel']);
// http://example.com/posts?sort=latest&search=Laravel
If the query parameter already exists, Laravel overwrites it:
echo url()->query('/posts?sort=latest', ['sort' => 'oldest']);
// http://example.com/posts?sort=oldest
✅ Arrays are also supported:
$url = url()->query('/posts', ['columns' => ['title', 'body']]);
echo $url;
// http://example.com/posts?columns%5B0%5D=title&columns%5B1%5D=body
echo urldecode($url);
// http://example.com/posts?columns[0]=title&columns[1]=body
Accessing Current, Full, and Previous URLs
// Current URL without query string
echo url()->current();
// Current URL with query string
echo url()->full();
// Previous full URL
echo url()->previous();
// Previous request path only
echo url()->previousPath();
Alternatively, use the URL facade:
use Illuminate\Support\Facades\URL;
echo URL::current();
URLs for Named Routes
Why Use Named Routes?
Hardcoding URLs is error-prone. If the route changes, you must update it everywhere. Instead, use named routes.
Route::get('/post/{post}', function (Post $post) {
// ...
})->name('post.show');
Now you can generate the URL with:
echo route('post.show', ['post' => 1]);
// http://example.com/post/1
Routes with Multiple Parameters
Route::get('/post/{post}/comment/{comment}', function (Post $post, Comment $comment) {
// ...
})->name('comment.show');
echo route('comment.show', ['post' => 1, 'comment' => 3]);
// http://example.com/post/1/comment/3
Extra Query Parameters
Any extra array items become query strings:
echo route('post.show', ['post' => 1, 'search' => 'rocket']);
// http://example.com/post/1?search=rocket
Passing Eloquent Models
The route() helper automatically extracts the route key from models:
echo route('post.show', ['post' => $post]);
// http://example.com/post/1
Signed URLs
Signed URLs prevent tampering with query strings by appending a hash.
Generating a Signed URL
use Illuminate\Support\Facades\URL;
return URL::signedRoute('unsubscribe', ['user' => 1]);
Exclude domain from hash:
URL::signedRoute('unsubscribe', ['user' => 1], absolute: false);
Temporary Signed URL
Expire the link after a time limit:
return URL::temporarySignedRoute(
'unsubscribe',
now()->addMinutes(30),
['user' => 1]
);
Validating Signed Requests
use Illuminate\Http\Request;
Route::get('/unsubscribe/{user}', function (Request $request) {
if (! $request->hasValidSignature()) {
abort(401);
}
});
Allow certain query params to be ignored:
if (! $request->hasValidSignatureWhileIgnoring(['page', 'order'])) {
abort(401);
}
Using Middleware for Validation
Instead of checking manually:
Route::post('/unsubscribe/{user}', function () {
// ...
})->name('unsubscribe')->middleware('signed');
If using relative signed URLs:
->middleware('signed:relative');
Handling Expired Links
Customize error for invalid signature:
use Illuminate\Routing\Exceptions\InvalidSignatureException;
->withExceptions(function ($exceptions) {
$exceptions->render(function (InvalidSignatureException $e) {
return response()->view('errors.link-expired', status: 403);
});
});
URLs for Controller Actions
Use action() for controller routes:
use App\Http\Controllers\HomeController;
$url = action([HomeController::class, 'index']);
With parameters:
$url = action([UserController::class, 'profile'], ['id' => 1]);
Fluent URI Objects
Laravel’s Uri class provides an object-based approach to build and modify URLs.
Creating URIs
use Illuminate\Support\Uri;
// From a string
$uri = Uri::of('https://example.com/path');
// From Laravel helpers
$uri = Uri::to('/dashboard');
$uri = Uri::route('users.show', ['user' => 1]);
$uri = Uri::signedRoute('users.show', ['user' => 1]);
$uri = Uri::temporarySignedRoute('user.index', now()->addMinutes(5));
$uri = Uri::action([UserController::class, 'index']);
Modifying URIs
$uri = Uri::of('https://example.com')
->withScheme('http')
->withHost('test.com')
->withPort(8000)
->withPath('/users')
->withQuery(['page' => 2])
->withFragment('section-1');
Default URL Parameters
You can set default values for URL parameters across a request.
Example: Default locale
Route::get('/{locale}/posts', function () {
// ...
})->name('post.index');
Define defaults in a middleware:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\URL;
class SetDefaultLocaleForUrls
{
public function handle($request, Closure $next)
{
URL::defaults(['locale' => $request->user()->locale]);
return $next($request);
}
}
Now you don’t need to pass locale every time.
Middleware Priority
Because default values can conflict with model binding, ensure this middleware runs before SubstituteBindings:
->withMiddleware(function ($middleware) {
$middleware->prependToPriorityList(
before: \Illuminate\Routing\Middleware\SubstituteBindings::class,
prepend: \App\Http\Middleware\SetDefaultLocaleForUrls::class,
);
});
Important
Laravel provides flexible URL generation helpers that make your app more dynamic and maintainable.
- Use
url()for simple paths and queries. - Use
route()for named routes (best practice). - Use signed URLs for security.
- Use
action()for controller links. - Use
Urifor object-oriented URL building. - Use
URL::defaults()to define global parameters.

