When your company goes through a rebrand or domain migration, one of the most critical technical tasks is ensuring all traffic from old domains seamlessly redirects to your new domain. But there's a catch: if you're not careful, you can break SSL certificate validation and prevent Certbot from renewing your certificates.
In this guide, I'll show you how to create a Laravel 12 global middleware that handles multiple old domain redirects while keeping Certbot happy.
The Challenge
We needed to accomplish several things:
Redirect all traffic from multiple old domains to a single new domain
Preserve the entire URL structure (paths and query parameters)
Use 301 (permanent) redirects for SEO benefits
Allow Certbot/Let's Encrypt validation requests to pass through without redirection
Make it work globally across the entire Laravel application
The Solution: A Custom Global Middleware
Laravel 12's middleware system is perfect for this use case. Here's the complete middleware implementation:
Step 1: Create the Middleware
Create a new file at app/Http/Middleware/RedirectToNewDomain.php:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class RedirectToNewDomain
{
/**
* The old domains to redirect from
*/
protected array $oldDomains = [
'old-domain.com',
'www.old-domain.com',
'another-old-domain.com',
'www.another-old-domain.com',
];
/**
* The new domain to redirect to
*/
protected string $newDomain = 'new-domain.com';
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
// Allow Certbot validation requests to pass through
if ($this->isCertbotRequest($request)) {
return $next($request);
}
// Check if the request is coming from any of the old domains
if (in_array($request->getHost(), $this->oldDomains)) {
// Build the new URL with the same path and query parameters
$newUrl = $request->getScheme() . '://' . $this->newDomain . $request->getRequestUri();
// Perform a 301 permanent redirect
return redirect($newUrl, 301);
}
return $next($request);
}
/**
* Determine if the request is from Certbot for SSL validation
*/
protected function isCertbotRequest(Request $request): bool
{
// Check if the request path starts with /.well-known/acme-challenge/
if ($request->is('.well-known/acme-challenge/*')) {
return true;
}
// Optional: Also check User-Agent for Certbot
$userAgent = $request->userAgent();
if ($userAgent && str_contains(strtolower($userAgent), 'certbot')) {
return true;
}
return false;
}
}
Step 2: Register the Middleware Globally
In Laravel 12, middleware registration happens in bootstrap/app.php. Open that file and add your middleware using the prepend() method:
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
// Domain redirect middleware - runs first
$middleware->prepend(\App\Http\Middleware\RedirectToNewDomain::class);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();
Step 3: Configure Your Domains
Update the $oldDomains array with all your legacy domains and set the $newDomain to your new domain:
protected array $oldDomains = [
'youroldsite.com',
'www.youroldsite.com',
'legacy-brand.com',
'www.legacy-brand.com',
];
protected string $newDomain = 'yournewsite.com';
How It Works
The middleware operates with a simple but effective logic flow:
Certbot Check First: Before doing anything else, it checks if the request is from Certbot by examining the URL path (
.well-known/acme-challenge/*) and optionally the user agent. If it's a Certbot request, it allows it through without redirection.Domain Matching: It checks if the incoming request's host matches any domain in the
$oldDomainsarray.URL Preservation: If there's a match, it constructs a new URL using the same scheme (HTTP/HTTPS), the new domain, and the complete request URI (which includes the path and all query parameters).
301 Redirect: It performs a permanent redirect (301) to signal to search engines that this is a permanent move.
Why Use prepend() Instead of append()?
When registering the middleware, we use prepend() rather than append(). This ensures the redirect happens as early as possible in the request lifecycle, before other middleware processes the request. This is more efficient and prevents unnecessary processing of requests that will ultimately be redirected anyway.
Making It Environment-Aware (Optional)
If you want to make the domains configurable via environment variables, you can modify the middleware constructor:
protected array $oldDomains;
protected string $newDomain;
public function __construct()
{
$this->oldDomains = explode(',', config('app.old_domains', 'old-domain.com'));
$this->newDomain = config('app.new_domain', 'new-domain.com');
}
Then add to config/app.php:
'old_domains' => env('OLD_DOMAINS', 'old-domain.com,www.old-domain.com'),
'new_domain' => env('NEW_DOMAIN', 'new-domain.com'),
And in your .env file:
OLD_DOMAINS="oldsite.com,www.oldsite.com,legacy.com"
NEW_DOMAIN="newsite.com"
Testing Your Implementation
To verify everything works correctly:
Test redirects: Visit your old domains and confirm they redirect to the new domain with paths and query parameters intact.
Example:
http://old-domain.com/blog/post?id=123should redirect tohttp://new-domain.com/blog/post?id=123
Test Certbot: Ensure SSL certificate renewal still works by checking that
/.well-known/acme-challenge/requests on old domains are not redirected.Check redirect type: Use browser developer tools or a tool like
curl -Ito confirm the response code is 301 (permanent redirect).
SEO Considerations
Using 301 redirects is crucial for maintaining your search engine rankings. This tells search engines that:
The move is permanent
All link equity should transfer to the new domain
The old URLs should be replaced with new ones in their index
Make sure to also update your Google Search Console and other webmaster tools to reflect the domain change.
Conclusion
With this simple middleware solution, you can handle complex multi-domain redirects in Laravel 12 while maintaining SSL certificate functionality. The implementation is clean, maintainable, and follows Laravel best practices.
The key takeaways:
Use global middleware for domain-wide redirects
Always preserve URL structure for user experience and SEO
Protect Certbot validation paths to maintain SSL certificates
Use 301 redirects for permanent domain changes
Register the middleware with
prepend()for early execution
Have you implemented domain redirects in Laravel? What challenges did you face? Let me know in the comments below!