Skip to content

Add client-side fallback for trailing-slash mismatches - #719

Open
louisachu wants to merge 2 commits into
stagefrom
devsite-2506-test
Open

Add client-side fallback for trailing-slash mismatches#719
louisachu wants to merge 2 commits into
stagefrom
devsite-2506-test

Conversation

@louisachu

@louisachu louisachu commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

EDS treats a folder-index page and a direct-file page as distinct resources based on the trailing slash, unlike Gatsby which tolerated either form. External links/bookmarks predating the migration often use the wrong form and 404 even though the page exists under the other form.

The connector (devsite-runtime-connector) already returns a real 301 for this case, but Helix's content-bus integration discards non-200 responses from custom content sources and substitutes its own generic 404, so the redirect never reaches real users. This client-side fallback checks, from the 404 page, whether the sibling slash-form resolves and redirects there if so - it runs after the curated redirects.json lookup so intentional page-move redirects still take priority.

Before:
https://stage--adp-devsite-stage--adobedocs.aem.page/dev-docs-reference/blocks/table

Now:
https://devsite-2506-test--adp-devsite-stage--adobedocs.aem.page/dev-docs-reference/blocks/table will redirect to
https://devsite-2506-test--adp-devsite-stage--adobedocs.aem.page/dev-docs-reference/blocks/table/

bug:
https://jira.corp.adobe.com/browse/DEVSITE-2506

EDS treats a folder-index page and a direct-file page as distinct
resources based on the trailing slash, unlike Gatsby which tolerated
either form. External links/bookmarks predating the migration often
use the wrong form and 404 even though the page exists under the
other form.

The connector (devsite-runtime-connector) already returns a real 301
for this case, but Helix's content-bus integration discards non-200
responses from custom content sources and substitutes its own generic
404, so the redirect never reaches real users. This client-side
fallback checks, from the 404 page, whether the sibling slash-form
resolves and redirects there if so - it runs after the curated
redirects.json lookup so intentional page-move redirects still take
priority.
@aem-code-sync

aem-code-sync Bot commented Aug 6, 2026

Copy link
Copy Markdown

Hello, I'm the AEM Code Sync Bot and I will run some actions to deploy your branch and validate page speed.
In case there are problems, just click a checkbox below to rerun the respective action.

  • Re-run all PSI checks
  • Re-run failed PSI checks
  • Re-sync branch
Commits

@aem-code-sync

aem-code-sync Bot commented Aug 6, 2026

Copy link
Copy Markdown
Page Scores Audits Google
📱 /dev-docs-reference/blocks/table Lighthouse returned error: FAILED_DOCUMENT_REQUEST. Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests. (Details: net::ERR_ABORTED) PSI
🖥️ /dev-docs-reference/blocks/table Lighthouse returned error: FAILED_DOCUMENT_REQUEST. Lighthouse was unable to reliably load the page you requested. Make sure you are testing the correct URL and that the server is properly responding to all requests. (Details: net::ERR_ABORTED) PSI
📱 /dev-docs-reference/blocks/table/ PERFORMANCE A11Y SEO BEST PRACTICES SI FCP LCP TBT CLS PSI
🖥️ /dev-docs-reference/blocks/table/ PERFORMANCE A11Y SEO BEST PRACTICES SI FCP LCP TBT CLS PSI

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

❌ Test Results

Status: Some tests failed!

🔍 Click to view failed tests
📁 test/blocks/code/code.test.js:

❌ Code block > code > data-playground attributes from class
      AssertionError: expected null to equal 'code-session'
        at n.<anonymous> (test/blocks/code/code.test.js:29:63)


📁 test/blocks/columns/columns.test.js:

❌ Columns block > Columns > columns-container
      AssertionError: expected false to be true
      + expected - actual
      
      -false
      +true
      
      at n.<anonymous> (test/blocks/columns/columns.test.js:29:117)

❌ Columns block > Columns > buttons
      AssertionError: expected false to be true
      + expected - actual
      
      -false
      +true
      
      at test/blocks/columns/columns.test.js:100:86
      at NodeList.forEach (<anonymous>)
      at n.<anonymous> (test/blocks/columns/columns.test.js:95:50)


📁 test/blocks/embed/embed.test.js:

❌ Could not import your test module. Check the browser logs or open the browser in debug mode for more information.


📁 test/blocks/contributors/contributors.test.js:

❌ Contributors block > contributors > firstDiv
      AssertionError: expected null to exist
        at n.<anonymous> (test/blocks/contributors/contributors.test.js:32:28)

❌ Contributors block > contributors > remove divs without children
      AssertionError: expected false to be true
      + expected - actual
      
      -false
      +true
      
      at test/blocks/contributors/contributors.test.js:42:44
      at NodeList.forEach (<anonymous>)
      at n.<anonymous> (test/blocks/contributors/contributors.test.js:41:51)

❌ Contributors block > contributors > last update div
      AssertionError: expected '' to equal 'https://github.com/AdobeDocs/express-add-ons-docs/commits/main/src/pages/references/index.md'
      + expected - actual
      
      +https://github.com/AdobeDocs/express-add-ons-docs/commits/main/src/pages/references/index.md
      
      at n.<anonymous> (test/blocks/contributors/contributors.test.js:53:40)

❌ Contributors block > contributors > image list div
      AssertionError: expected null to exist
        at test/blocks/contributors/contributors.test.js:71:27
        at Array.forEach (<anonymous>)
        at n.<anonymous> (test/blocks/contributors/contributors.test.js:69:33)


📁 test/blocks/tab/tab-playground.test.js:

❌ Tab block playground metadata > extracts data-playground attributes via decoratePreformattedCode
      AssertionError: expected null to equal 'tab-session'
        at n.<anonymous> (test/blocks/tab/tab-playground.test.js:17:63)


📁 test/blocks/tab/tab.test.js:

❌ Tab block > Tab Button Structure > tab > button load structure
      AssertionError: expected 'tab-button active' to equal 'tab-button'
      + expected - actual
      
      -tab-button active
      +tab-button
      
      at n.<anonymous> (test/blocks/tab/tab.test.js:60:38)

❌ Tab block > Sub-tabs > sub-tab > attributes
      AssertionError: expected 'subTab1' to equal 'subTab3'
      + expected - actual
      
      -subTab1
      +subTab3
      
      at test/blocks/tab/tab.test.js:135:58
      at NodeList.forEach (<anonymous>)
      at n.<anonymous> (test/blocks/tab/tab.test.js:133:23)



Test Coverage Report

Overall Coverage Summary

Metric Percentage Coverage
Statements 57.98% 3890/6709
Branches 86.02% 560/651
Functions 52.2% 130/249
Lines 57.98% 3890/6709

Coverage by File/Directory

File Statements Branches Functions Lines
blocks/accordion 96.73% 100% 100% 96.73%
blocks/announcement 76.69% 60% 100% 76.69%
blocks/banner 94.28% 62.5% 100% 94.28%
blocks/cards 92.04% 90% 100% 92.04%
blocks/carousel 77.8% 82.35% 71.42% 77.8%
blocks/code 81.81% 60% 100% 81.81%
blocks/columns 62.59% 75% 100% 62.59%
blocks/contributors 84.23% 56% 100% 84.23%
blocks/edition 91.37% 57.14% 100% 91.37%
blocks/fragment 17.03% 100% 0% 17.03%
blocks/image-text 57.5% 55.55% 100% 57.5%
blocks/info 96.87% 100% 100% 96.87%
blocks/info-card 100% 100% 100% 100%
blocks/info-columns 75% 100% 100% 75%
blocks/list 59.09% 85.71% 100% 59.09%
blocks/mini-resource-card 98% 87.5% 100% 98%
blocks/product-card 78.57% 94.11% 100% 78.57%
blocks/profile-card 96.29% 100% 100% 96.29%
blocks/site-hero 93.93% 80% 100% 93.93%
blocks/summary 98.36% 88.88% 100% 98.36%
blocks/tab 93.25% 96.87% 100% 93.25%
blocks/table 100% 84.61% 100% 100%
blocks/text 50% 83.33% 50% 50%
blocks/title 84% 66.66% 100% 84%
components 56.83% 73.52% 58.33% 56.83%
scripts 48.61% 95.22% 42.55% 48.61%

Coverage report generated at 2026-08-06T00:08:33.053Z

async function redirectOnTrailingSlashMismatch() {
const candidate = toggleTrailingSlash(window.location.pathname);
const resp = await fetch(candidate);
if (resp.ok) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const resp = await fetch(candidate, { method: 'HEAD' }); can we do this instead of GET since only resp.ok is read and the body is never used.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

@priyajeettanwar priyajeettanwar left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@louisachu While I understand this is a client-side fix, I think the long-term fix should be in EDS/Helix.

As you mentioned in the PR, the devsite-runtime-connector already returns a proper 301, but Helix's content-bus discards non-200 responses from custom content sources and replaces them with a generic 404, so the redirect never reaches users.

The current approach works as a short-term workaround, but I think we should also push for an EDS/Helix fix so the original HTTP responses are preserved.

Only resp.ok is read, so HEAD avoids downloading the full page body
on every 404 hit while still guaranteeing the same status code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants