Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: redirect trailing slashes on on-demand rendered pages #12994

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

ascorbic
Copy link
Contributor

@ascorbic ascorbic commented Jan 16, 2025

Changes

When the trailingSlash option is set to always or never, on-demand rendered pages will now redirect to the correct URL when the trailing slash is incorrect. This was previously the case for static pages, but now works for on-demand pages as well. There are some exceptions:

  • when the request matches a static path or file, it is not redirected
  • when trailingSlash is ignore then it is not redirected
  • when the request looks like it is for a file, it is not redirected, even if there is no matching file. e.g. a request for a nonexistent /favicon.ico will not redirect to /favicon.ico/
  • requests for paths starting with /_ with not redirect, because these are usually special internal paths

In dev we don't redirect so it's more obvious that a link is incorrect. Instead I have updated the 404 page to give a bit more information if there's a trailing slash mismatch, and suggest goign to the version with the correct slash:

image

Fixes #12532
Fixes #12833
Fixes #11575

Testing

Added a big test suite. Refer to it for more examples.

Docs

This will need docs

Copy link

changeset-bot bot commented Jan 16, 2025

🦋 Changeset detected

Latest commit: 57feb05

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added pkg: astro Related to the core `astro` package (scope) semver: minor Change triggers a `minor` release labels Jan 16, 2025
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

This PR is blocked because it contains a minor changeset. A reviewer will merge this at the next release if approved.

Copy link

codspeed-hq bot commented Jan 16, 2025

CodSpeed Performance Report

Merging #12994 will not alter performance

Comparing trailing-magic (57feb05) with main (e36837f)

Summary

✅ 6 untouched benchmarks

packages/astro/src/core/app/index.ts Outdated Show resolved Hide resolved
packages/internal-helpers/src/path.ts Outdated Show resolved Hide resolved
packages/astro/src/core/app/index.ts Outdated Show resolved Hide resolved
packages/astro/src/core/app/index.ts Show resolved Hide resolved
packages/astro/src/core/app/index.ts Show resolved Hide resolved
@ascorbic
Copy link
Contributor Author

I wonder if this is a significant enough change to be behind an experimental flag.

@ematipico
Copy link
Member

ematipico commented Jan 16, 2025

I wonder if this is a significant enough change to be behind an experimental flag.

This features seems to be mostly a change for adapters. In this case, you might want to add a new Astro feature:

export type AstroAdapterFeatureMap = {
/**
* The adapter is able serve static pages
*/
staticOutput?: AdapterSupport;
/**
* The adapter is able to serve pages that are static or rendered via server
*/
hybridOutput?: AdapterSupport;
/**
* The adapter is able to serve SSR pages
*/
serverOutput?: AdapterSupport;
/**
* The adapter is able to support i18n domains
*/
i18nDomains?: AdapterSupport;
/**
* The adapter is able to support `getSecret` exported from `astro:env/server`
*/
envGetSecret?: AdapterSupport;
/**
* The adapter supports image transformation using the built-in Sharp image service
*/
sharpImageService?: AdapterSupport;
};

Like this, adapters can opt-in and "break" things as much as they want, using their own versioning. What do you think?

@ascorbic
Copy link
Contributor Author

I don't think so. This will work without any change on the adapter's part.

Copy link
Member

@bluwy bluwy left a comment

Choose a reason for hiding this comment

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

This makes sense to me 👍 I don't think this could break things so probably don't need an experimental option

packages/astro/src/vite-plugin-astro-server/base.ts Outdated Show resolved Hide resolved
@ematipico
Copy link
Member

ematipico commented Jan 16, 2025

I don't think so. This will work without any change on the adapter's part.

That I know. I was suggesting an experimental path without necessarily having one in core, since this is a change that affects only on-demand pages with an adapter, and I would prefer this way because we don't know if users have already fixed this on their end.

@matthewp
Copy link
Contributor

@ematipico why do you think this is breaking?

@ascorbic
Copy link
Contributor Author

!preview trailing-slash-redirect

Copy link
Contributor

Snapshots have been released for the following packages:

  • astro@experimental--trailing-slash-redirect
Publish Log
🦋  warn ===============================IMPORTANT!===============================
🦋  warn Packages will be released under the experimental--trailing-slash-redirect tag
🦋  warn ----------------------------------------------------------------------
🦋  info npm info astro
🦋  info npm info @astrojs/prism
🦋  info npm info @astrojs/rss
🦋  info npm info create-astro
🦋  info npm info @astrojs/db
🦋  info npm info @astrojs/alpinejs
🦋  info npm info @astrojs/markdoc
🦋  info npm info @astrojs/mdx
🦋  info npm info @astrojs/partytown
🦋  info npm info @astrojs/preact
🦋  info npm info @astrojs/react
🦋  info npm info @astrojs/sitemap
🦋  info npm info @astrojs/solid-js
🦋  info npm info @astrojs/svelte
🦋  info npm info @astrojs/tailwind
🦋  info npm info @astrojs/vue
🦋  info npm info @astrojs/web-vitals
🦋  info npm info @astrojs/internal-helpers
🦋  info npm info @astrojs/markdown-remark
🦋  info npm info @astrojs/studio
🦋  info npm info @astrojs/telemetry
🦋  info npm info @astrojs/underscore-redirects
🦋  info npm info @astrojs/upgrade
🦋  info astro is being published because our local version (0.0.0-trailing-slash-redirect-20250121123511) has not been published on npm
🦋  warn @astrojs/prism is not being published because version 3.2.0 is already published on npm
🦋  warn @astrojs/rss is not being published because version 4.0.11 is already published on npm
🦋  warn create-astro is not being published because version 4.11.0 is already published on npm
🦋  warn @astrojs/db is not being published because version 0.14.5 is already published on npm
🦋  warn @astrojs/alpinejs is not being published because version 0.4.1 is already published on npm
🦋  warn @astrojs/markdoc is not being published because version 0.12.6 is already published on npm
🦋  warn @astrojs/mdx is not being published because version 4.0.6 is already published on npm
🦋  warn @astrojs/partytown is not being published because version 2.1.3 is already published on npm
🦋  warn @astrojs/preact is not being published because version 4.0.2 is already published on npm
🦋  warn @astrojs/react is not being published because version 4.1.5 is already published on npm
🦋  warn @astrojs/sitemap is not being published because version 3.2.1 is already published on npm
🦋  warn @astrojs/solid-js is not being published because version 5.0.3 is already published on npm
🦋  warn @astrojs/svelte is not being published because version 7.0.3 is already published on npm
🦋  warn @astrojs/tailwind is not being published because version 5.1.4 is already published on npm
🦋  warn @astrojs/vue is not being published because version 5.0.5 is already published on npm
🦋  warn @astrojs/web-vitals is not being published because version 3.0.1 is already published on npm
🦋  warn @astrojs/internal-helpers is not being published because version 0.4.2 is already published on npm
🦋  warn @astrojs/markdown-remark is not being published because version 6.0.2 is already published on npm
🦋  warn @astrojs/studio is not being published because version 0.1.3 is already published on npm
🦋  warn @astrojs/telemetry is not being published because version 3.2.0 is already published on npm
🦋  warn @astrojs/underscore-redirects is not being published because version 0.6.0 is already published on npm
🦋  warn @astrojs/upgrade is not being published because version 0.4.3 is already published on npm
🦋  info Publishing "astro" at "0.0.0-trailing-slash-redirect-20250121123511"
🦋  success packages published successfully:
🦋  [email protected]
🦋  Creating git tag...
🦋  New tag:  [email protected]
Build Log

> [email protected] build /home/runner/work/astro/astro
> turbo run build --filter=astro --filter=create-astro --filter="@astrojs/*" --filter="@benchmark/*"

• Packages in scope: @astrojs/alpinejs, @astrojs/cloudflare, @astrojs/db, @astrojs/internal-helpers, @astrojs/markdoc, @astrojs/markdown-remark, @astrojs/mdx, @astrojs/netlify, @astrojs/node, @astrojs/partytown, @astrojs/preact, @astrojs/prism, @astrojs/react, @astrojs/rss, @astrojs/sitemap, @astrojs/solid-js, @astrojs/studio, @astrojs/svelte, @astrojs/tailwind, @astrojs/telemetry, @astrojs/underscore-redirects, @astrojs/upgrade, @astrojs/vercel, @astrojs/vue, @astrojs/web-vitals, @benchmark/adapter, @benchmark/timer, astro, create-astro
• Running build in 29 packages
• Remote caching enabled
::group::@astrojs/telemetry:build
cache miss, executing d876d471dc4084c3

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/telemetry
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::
::group::@astrojs/internal-helpers:build
cache miss, executing a2645d5c274fb4f7

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/internal-helpers
> astro-scripts build "src/**/*.ts" && tsc -p tsconfig.json

::endgroup::
::group::create-astro:build
cache miss, executing a513ef99a7061599

> [email protected] build /home/runner/work/astro/astro/packages/create-astro
> astro-scripts build "src/index.ts" --bundle && tsc

::endgroup::
::group::@astrojs/upgrade:build
cache miss, executing d633ad364f73ba09

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/upgrade
> astro-scripts build "src/index.ts" --bundle && tsc

::endgroup::
::group::@astrojs/prism:build
cache miss, executing 03f2919d025425f4

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/astro-prism
> astro-scripts build "src/**/*.ts" && tsc -p ./tsconfig.json

::endgroup::
::group::@astrojs/markdown-remark:build
cache miss, executing 765afdc94755514e

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/markdown/remark
> astro-scripts build "src/**/*.ts" && tsc -p tsconfig.json

::endgroup::
::group::astro:build
cache miss, executing 317f0e9d79a67fe0

> [email protected] build /home/runner/work/astro/astro/packages/astro
> pnpm run prebuild && astro-scripts build "src/**/*.{ts,js}" --copy-wasm && tsc


> [email protected] prebuild /home/runner/work/astro/astro/packages/astro
> astro-scripts prebuild --to-string "src/runtime/server/astro-island.ts" "src/runtime/client/{idle,load,media,only,visible}.ts"

::endgroup::
::group::@astrojs/studio:build
cache miss, executing 3777e5fa0a5bcbe8

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/studio
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::
::group::@astrojs/alpinejs:build
cache miss, executing ccd4db1e1aa94a59

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/integrations/alpinejs
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::
::group::@astrojs/rss:build
cache miss, executing 4efd35f98a9c20d4

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/astro-rss
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::
::group::@astrojs/partytown:build
cache miss, executing eb6045f12825aae5

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/integrations/partytown
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::
::group::@benchmark/adapter:build
cache miss, executing 3c7b39c696d7dc86

> @benchmark/[email protected] build /home/runner/work/astro/astro/benchmark/packages/adapter
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::
::group::@astrojs/svelte:build
cache miss, executing 7441636da8213514

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/integrations/svelte
> astro-scripts build "src/index.ts" && astro-scripts build "src/editor.cts" --force-cjs --no-clean-dist && tsc

::endgroup::
::group::@astrojs/react:build
cache miss, executing 94fda5aa6d00ecf1

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/integrations/react
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::
::group::@astrojs/tailwind:build
cache miss, executing 9f42babf21ae4520

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/integrations/tailwind
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::
::group::@astrojs/vue:build
cache miss, executing bd1b260a8dbb0b18

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/integrations/vue
> astro-scripts build "src/index.ts" && astro-scripts build "src/editor.cts" --force-cjs --no-clean-dist && tsc

::endgroup::
::group::@astrojs/mdx:build
cache miss, executing 3bc5a795432ddb38

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/integrations/mdx
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::
::group::@benchmark/timer:build
cache miss, executing 72027d5354b9c330

> @benchmark/[email protected] build /home/runner/work/astro/astro/benchmark/packages/timer
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::
::group::@astrojs/underscore-redirects:build
cache miss, executing b9667b3402a7facb

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/underscore-redirects
> astro-scripts build "src/**/*.ts" && tsc -p tsconfig.json

::endgroup::
::group::@astrojs/markdoc:build
cache miss, executing b6b03356271c58d7

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/integrations/markdoc
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::
::group::@astrojs/sitemap:build
cache miss, executing e7919ab24e15764e

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/integrations/sitemap
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::
::group::@astrojs/solid-js:build
cache miss, executing 63df8cdba110cc70

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/integrations/solid
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::
::group::@astrojs/preact:build
cache miss, executing d2c12e381ec2fe94

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/integrations/preact
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::
::group::@astrojs/db:build
cache miss, executing e9382d391d1c0ee9

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/db
> astro-scripts build "src/**/*.ts" && tsc && pnpm types:virtual


> @astrojs/[email protected] types:virtual /home/runner/work/astro/astro/packages/db
> tsc -p ./tsconfig.virtual.json

::endgroup::
::group::@astrojs/web-vitals:build
cache miss, executing 9c9e132a2421953e

> @astrojs/[email protected] build /home/runner/work/astro/astro/packages/integrations/web-vitals
> astro-scripts build "src/**/*.ts" && tsc

::endgroup::

 Tasks:    25 successful, 25 total
Cached:    0 cached, 25 total
  Time:    46.75s 

@ematipico ematipico added this to the 5.2.0 milestone Jan 24, 2025
@sarah11918
Copy link
Member

For docs, would just suggest taking a quick peek to make sure nothing here affects what we say in these places:

Copy link
Member

@sarah11918 sarah11918 left a comment

Choose a reason for hiding this comment

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

Just noting that when I look at this entry as a whole, I think we might be at the point where a restructuring is needed:

  • the first line mentions that this specifically is to configure how the dev server works, but there is also talk about "in production" below that's not particularly separated out/scannable for someone to find their use case
  • we clearly define the 3 options, but then in normal paragraph text give more specifics about some of the options. This might mean that we need to separate each of the 3 options and give them all their own content
  • there is a difference in how prerendered vs on demand works that's not separated out/scannable for someone to find their use case

Instead of adding on more notes/caveats, can we think of a better way to organize all this so people will be better easier to find the information they need quickly? Any thoughts on this?

title: 'Not found',
tabTitle: '404: Not Found',
body: `<p>Your site is configured with <code>trailingSlash</code> set to <code>${trailingSlash}</code>. Do you want to go to <a href="${corrected}">${corrected}</a> instead?</p>
<p>Come to our <a href="https://astro.build/chat">Discord</a> if you need help.</p>`,
Copy link
Member

Choose a reason for hiding this comment

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

Not opposed to also a link to Discord for help, but perhaps a docs link to config reference for trailingSlash is more directly helpful?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated. The message for base (which is what I copied this from) should probably also be updated, but that can go ina different PR

@ascorbic
Copy link
Contributor Author

@sarah11918 I totally missed that this said it was just about dev server. That's already incorrect: it's used in production too.

@ascorbic
Copy link
Contributor Author

@sarah11918 how about now

Comment on lines +240 to +242
* - `'ignore'` - Match URLs regardless of whether a trailing "/" exists. Requests for "/foo" and "/foo/" will both match the same route.
* - `'always'` - Only match URLs that **include** a trailing slash (e.g: "/foo/"). In production, requests for URLs *without* a trailing slash will be redirected to the correct URL. In development, requests for URLs *without* a trailing slash will display a warning page.
* - `'never'` - Only match URLs that **do not include** a trailing slash (e.g: "/foo"). In production, requests for URLs *with* a trailing slash will be redirected to the correct URL. In development, requests for URLs *with* a trailing slash will display a warning page.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* - `'ignore'` - Match URLs regardless of whether a trailing "/" exists. Requests for "/foo" and "/foo/" will both match the same route.
* - `'always'` - Only match URLs that **include** a trailing slash (e.g: "/foo/"). In production, requests for URLs *without* a trailing slash will be redirected to the correct URL. In development, requests for URLs *without* a trailing slash will display a warning page.
* - `'never'` - Only match URLs that **do not include** a trailing slash (e.g: "/foo"). In production, requests for URLs *with* a trailing slash will be redirected to the correct URL. In development, requests for URLs *with* a trailing slash will display a warning page.
* - `'ignore'` - Match URLs regardless of whether a trailing "/" exists. Requests for "/about" and "/about/" will both match the same route.
* - `'always'` - Only match URLs that include a trailing slash (e.g: "/about/"). In production, requests for on-demand rendered URLs without a trailing slash will be redirected to the correct URL for your convenience. However, in development, they will display a warning page reminding you that you have `always` configured.
* - `'never'` - Only match URLs that do not include a trailing slash (e.g: "/about"). In production, requests for on-demand rendered URLs with a trailing slash will be redirected to the correct URL for your convenience. However, in development, they will display a warning page reminding you that you have `never` configured.

I think much better! (And, now correct? 😅 )

Just tried something above that I'm not totally sold on, but I think provides a helpful nuance. This is my thinking, and maybe some different text gets at this better:

  • taking this opportunity to exorcise yet another "foo" from our examples!
  • re-iterating the on-demandness of production (lest someone not read below)
  • Since we're talking about the same URLs in both sentences, probably an easier read to "However, they..." without making them read the mouthful again
  • Bold/italics are generally not used for emphasis. Bold is OK when absolutely necessary and someone's eye needs to be drawn somewhere, but as a rule, italics isn't great for accessibility in docs and shouldn't really be used. (In this case, I also removed bold because it's not e.g. we want someone scanning the page to hit "with" -- that in and of itself isn't a key or meaningful idea.)
  • I thought it was helpful to say why they'd get a warning page; what the purpose is. This is admittedly making things longer, though. I would ideally want to say e.g., "so that you will notice when internal links contradict your configuration" but wondered whether we could get away with something like above

* Use this configuration option if your production host has strict handling of how trailing slashes work or do not work.
*
* You can also set this if you prefer to be more strict yourself, so that URLs with or without trailing slashes won't work during development.
* Note that for prerendered pages, trailing slashes are handled by the hosting platform, so may not behave in the same way.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* Note that for prerendered pages, trailing slashes are handled by the hosting platform, so may not behave in the same way.
* Trailing slashes on prerendered pages are handled by the hosting platform, and may not respect your chosen configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs pr pkg: astro Related to the core `astro` package (scope) semver: minor Change triggers a `minor` release
Projects
None yet
5 participants