From f410612aa25250573458679903344ca929a0ece6 Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Tue, 7 Apr 2026 09:04:15 +1000 Subject: [PATCH 1/3] Add 301 redirect for site-prefixed URLs --- .../TideSiteRequestEventSubscriber.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/tide_site/src/EventSubscriber/TideSiteRequestEventSubscriber.php b/modules/tide_site/src/EventSubscriber/TideSiteRequestEventSubscriber.php index 5e6e85488..3dee4ee9c 100644 --- a/modules/tide_site/src/EventSubscriber/TideSiteRequestEventSubscriber.php +++ b/modules/tide_site/src/EventSubscriber/TideSiteRequestEventSubscriber.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -109,6 +110,16 @@ public function onRequestAddSiteFilter(RequestEvent $event) { $path = $api_helper->getRequestedPath($request); // Only prefix non-homepage and unrouted path. if ($path !== '/') { + // If the path already has a site prefix, 301 redirect to the clean + // path so the FE uses canonical URLs without site prefixes. + if ($this->helper->hasSitePrefix($path)) { + $clean_path = preg_replace('#^/site-\d+/#', '/', $path); + $query = $request->query->all(); + $query['path'] = $clean_path; + $redirect_url = $request->getBaseUrl() . $request->getPathInfo() . '?' . http_build_query($query); + $event->setResponse(new RedirectResponse($redirect_url, Response::HTTP_MOVED_PERMANENTLY)); + return; + } try { $url = Url::fromUri('internal:' . $path); if (!$url->isRouted() && !$this->helper->hasSitePrefix($path)) { From ca1fc344efe80d52a6f6180b9c202b1b4626ded7 Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Thu, 9 Apr 2026 14:40:15 +1000 Subject: [PATCH 2/3] supports /node/1234 redirect to 301 --- .../TideSiteRequestEventSubscriber.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/tide_site/src/EventSubscriber/TideSiteRequestEventSubscriber.php b/modules/tide_site/src/EventSubscriber/TideSiteRequestEventSubscriber.php index 3dee4ee9c..70e75e615 100644 --- a/modules/tide_site/src/EventSubscriber/TideSiteRequestEventSubscriber.php +++ b/modules/tide_site/src/EventSubscriber/TideSiteRequestEventSubscriber.php @@ -120,6 +120,20 @@ public function onRequestAddSiteFilter(RequestEvent $event) { $event->setResponse(new RedirectResponse($redirect_url, Response::HTTP_MOVED_PERMANENTLY)); return; } + // If the path is an internal node path (e.g. /node/1234), resolve + // to its alias and 301 redirect to the canonical URL. + if (preg_match('#^/node/\d+$#', $path)) { + /** @var \Drupal\path_alias\AliasManagerInterface $alias_manager */ + $alias_manager = $this->container->get('path_alias.manager'); + $alias = $alias_manager->getAliasByPath($path); + if ($alias && $alias !== $path) { + $query = $request->query->all(); + $query['path'] = $alias; + $redirect_url = $request->getBaseUrl() . $request->getPathInfo() . '?' . http_build_query($query); + $event->setResponse(new RedirectResponse($redirect_url, Response::HTTP_MOVED_PERMANENTLY)); + return; + } + } try { $url = Url::fromUri('internal:' . $path); if (!$url->isRouted() && !$this->helper->hasSitePrefix($path)) { From 7d7c7acf8cff4e0ea25d55c7f1c4a2322ce83639 Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Thu, 9 Apr 2026 15:34:20 +1000 Subject: [PATCH 3/3] follow redirect module's format. --- .../TideSiteRequestEventSubscriber.php | 50 ++++++++++++++----- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/modules/tide_site/src/EventSubscriber/TideSiteRequestEventSubscriber.php b/modules/tide_site/src/EventSubscriber/TideSiteRequestEventSubscriber.php index 70e75e615..68e80a38a 100644 --- a/modules/tide_site/src/EventSubscriber/TideSiteRequestEventSubscriber.php +++ b/modules/tide_site/src/EventSubscriber/TideSiteRequestEventSubscriber.php @@ -15,7 +15,6 @@ use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -110,27 +109,22 @@ public function onRequestAddSiteFilter(RequestEvent $event) { $path = $api_helper->getRequestedPath($request); // Only prefix non-homepage and unrouted path. if ($path !== '/') { - // If the path already has a site prefix, 301 redirect to the clean - // path so the FE uses canonical URLs without site prefixes. + // If the path already has a site prefix, return a redirect response + // in the same format as Drupal redirect module so the FE can handle + // it with its existing redirect logic. if ($this->helper->hasSitePrefix($path)) { $clean_path = preg_replace('#^/site-\d+/#', '/', $path); - $query = $request->query->all(); - $query['path'] = $clean_path; - $redirect_url = $request->getBaseUrl() . $request->getPathInfo() . '?' . http_build_query($query); - $event->setResponse(new RedirectResponse($redirect_url, Response::HTTP_MOVED_PERMANENTLY)); + $this->setRedirectRouteResponse($event, $request, $clean_path); return; } // If the path is an internal node path (e.g. /node/1234), resolve - // to its alias and 301 redirect to the canonical URL. + // to its alias and return a redirect response. if (preg_match('#^/node/\d+$#', $path)) { /** @var \Drupal\path_alias\AliasManagerInterface $alias_manager */ $alias_manager = $this->container->get('path_alias.manager'); $alias = $alias_manager->getAliasByPath($path); if ($alias && $alias !== $path) { - $query = $request->query->all(); - $query['path'] = $alias; - $redirect_url = $request->getBaseUrl() . $request->getPathInfo() . '?' . http_build_query($query); - $event->setResponse(new RedirectResponse($redirect_url, Response::HTTP_MOVED_PERMANENTLY)); + $this->setRedirectRouteResponse($event, $request, $alias); return; } } @@ -305,6 +299,38 @@ protected function setEventErrorResponse(RequestEvent $event, $error_message, $c $event->stopPropagation(); } + /** + * Set a redirect route JSON response matching Drupal redirect module format. + */ + protected function setRedirectRouteResponse(RequestEvent $event, Request $request, $redirect_url, $status_code = '301') { + $self_href = $request->getSchemeAndHttpHost() . $request->getRequestUri(); + /** @var \Drupal\Component\Uuid\UuidInterface $uuid_service */ + $uuid_service = $this->container->get('uuid'); + $json_response = [ + 'data' => [ + 'type' => 'route', + 'links' => [ + 'self' => [ + 'href' => $self_href, + ], + ], + 'id' => $uuid_service->generate(), + 'attributes' => [ + 'status_code' => $status_code, + 'redirect_url' => $redirect_url, + 'redirect_type' => 'internal', + ], + ], + 'links' => [ + 'self' => [ + 'href' => $self_href, + ], + ], + ]; + $response = new JsonResponse($json_response); + $event->setResponse($response); + } + /** * Helper to build field name for provided entity type. *