Installation:
composer require camelot/canonical-url-bundle
Ensure Camelot\CanonicalUrlBundle\CamelotCanonicalUrlBundle is enabled in config/bundles.php.
Configuration:
Add to config/packages/canonical_url.yaml:
camelot_canonical_url:
site_url: 'https://example.org' # Your primary domain
trailing_slash: true # Force trailing slashes (default: false)
www_redirect: true # Redirect www to non-www (default: false)
https_redirect: true # Force HTTPS (default: false)
First Use Case:
http://example.org/about-us → Should redirect to https://example.org/about-us/ (if configured).<link rel="canonical"> tag appears in Twig templates:
{{ canonical_url() }}
URL Normalization:
CanonicalUrlListener) to auto-redirect non-canonical URLs.http://www.example.org/page → https://example.org/page/.Twig Integration:
{{ canonical_url() }} in layouts (e.g., base.html.twig) to ensure consistency.# config/routes.yaml
about:
path: /about
controller: App\Controller\AboutController
canonical_url: 'https://example.org/about-us' # Custom canonical
Dynamic Canonical URLs:
CanonicalUrlGenerator service to generate URLs dynamically (e.g., for localized content):
// src/Service/CustomCanonicalUrlGenerator.php
class CustomCanonicalUrlGenerator extends CanonicalUrlGenerator {
public function generate(string $path, array $params = []): string {
// Custom logic (e.g., append locale)
return parent::generate($path, $params);
}
}
Register as a service with tags: ['canonical_url.generator'].API Responses:
Link: <https://example.org/resource>; rel="canonical"):
use Symfony\Component\HttpFoundation\Response;
return new Response(json_encode($data), 200, [
'Link' => '<https://example.org/resource>; rel="canonical"',
]);
symfony/routing to validate canonical URLs before redirects.Symfony\Component\HttpKernel\CacheWarmer).CanonicalUrlListener in PHPUnit to test redirects:
$listener = $this->createMock(CanonicalUrlListener::class);
$listener->method('onKernelRequest')->willReturnCallback(function ($event) {
$event->setResponse(new RedirectResponse('https://example.org'));
});
$container->set('canonical_url.listener', $listener);
Configuration Overrides:
config/packages/canonical_url.yaml must be merged with defaults. Omitting keys uses defaults (e.g., trailing_slash: false if not set).Route Conflicts:
/about vs /about/).canonical_url route option to override defaults:
about:
path: /about
canonical_url: 'https://example.org/about' # No trailing slash
HTTPS/SSL Issues:
https_redirect: true requires a valid SSL certificate. Misconfigurations may cause infinite redirects.https_redirect: false in staging, then enable in production.Twig Template Caching:
canonical_url() Twig function may break cached templates if the canonical URL changes dynamically.{{ app.canonical_url() }} (if available) or cache the function’s output.Subdomains:
blog.example.org). Redirects will fail if site_url is misconfigured.Check Redirects:
_profiler) to inspect the CanonicalUrlListener response.CanonicalUrlListener::onKernelRequest:
$this->logger->debug('Redirecting to canonical URL', ['url' => $canonicalUrl]);
Validate Canonical Tags:
<link rel="canonical"> exists.Custom Redirect Logic:
CanonicalUrlListener::getCanonicalUrl() to implement business rules (e.g., exclude certain paths):
public function getCanonicalUrl(Request $request): string {
if ($request->getPathInfo() === '/admin') {
return $request->getUri();
}
return parent::getCanonicalUrl($request);
}
Event Subscribers:
kernel.request to modify canonical URLs dynamically:
// src/EventListener/CustomCanonicalListener.php
class CustomCanonicalListener implements EventSubscriber {
public static function getSubscribedEvents() {
return ['kernel.request' => 'onKernelRequest'];
}
public function onKernelRequest(GetResponseEvent $event) {
$request = $event->getRequest();
if ($request->attributes->get('_route') === 'product') {
$request->attributes->set('canonical_url', 'https://example.org/products/' . $request->get('slug'));
}
}
}
API Headers:
ResponseEventSubscriber:
public function onKernelResponse(FilterResponseEvent $event) {
$response = $event->getResponse();
if ($response->headers->has('Content-Type') && strpos($response->headers->get('Content-Type'), 'application/json') !== false) {
$response->headers->set('Link', '<' . $this->canonicalUrl . '>; rel="canonical"');
}
}
Multi-Site Support:
site_url per environment:
# config/packages/canonical_url.yaml
camelot_canonical_url:
site_url: '%env(APP_CANONICAL_URL)%'
Set APP_CANONICAL_URL in .env.How can I help you explore Laravel packages today?