diff --git a/.changeset/six-grapes-look.md b/.changeset/six-grapes-look.md
new file mode 100644
index 000000000000..211d55122995
--- /dev/null
+++ b/.changeset/six-grapes-look.md
@@ -0,0 +1,7 @@
+---
+'astro': major
+---
+
+The value of `import.meta.env.BASE_URL`, which is derived from the `base` option, will not be enforced with a trailing slash if `trailingSlash: "ignore"` is set. The existing behaviours of `trailingSlash: "always"` and `trailingSlash: "never"` are unchanged.
+
+To migrate to this new behaviour, if your `base` configuration already has a trailing slash, no change is needed. If your `base` configuration does not have a trailing slash, you can add one to preserve the previous behaviour, or make sure any usages of `import.meta.env.BASE_URL` works without the trailing slash.
diff --git a/packages/astro/e2e/astro-envs.test.js b/packages/astro/e2e/astro-envs.test.js
index 1a4f4a1ca7aa..50cff6e89557 100644
--- a/packages/astro/e2e/astro-envs.test.js
+++ b/packages/astro/e2e/astro-envs.test.js
@@ -18,11 +18,11 @@ test.describe('Astro Environment BASE_URL', () => {
await page.goto(astro.resolveUrl('/blog/'));
const astroBaseUrl = page.locator('id=astro-base-url');
- await expect(astroBaseUrl, 'astroBaseUrl equals to /blog/').toHaveText('/blog/');
+ await expect(astroBaseUrl, 'astroBaseUrl equals to /blog').toHaveText('/blog');
const clientComponentBaseUrl = page.locator('id=client-component-base-url');
await expect(clientComponentBaseUrl, 'clientComponentBaseUrl equals to /blog').toHaveText(
- '/blog/'
+ '/blog'
);
});
});
diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts
index fbc88d0e48d5..6de339f3965e 100644
--- a/packages/astro/src/core/build/generate.ts
+++ b/packages/astro/src/core/build/generate.ts
@@ -28,6 +28,7 @@ import {
} from '../../core/build/internal.js';
import {
isRelativePath,
+ joinPaths,
prependForwardSlash,
removeLeadingForwardSlash,
removeTrailingForwardSlash,
@@ -437,11 +438,11 @@ function getUrlForPath(
buildPathname = base;
} else if (routeType === 'endpoint') {
const buildPathRelative = removeLeadingForwardSlash(pathname);
- buildPathname = base + buildPathRelative;
+ buildPathname = joinPaths(base, buildPathRelative);
} else {
const buildPathRelative =
removeTrailingForwardSlash(removeLeadingForwardSlash(pathname)) + ending;
- buildPathname = base + buildPathRelative;
+ buildPathname = joinPaths(base, buildPathRelative);
}
const url = new URL(buildPathname, origin);
return url;
diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts
index 282f7844e03d..b0a6b38e4d0c 100644
--- a/packages/astro/src/core/config/schema.ts
+++ b/packages/astro/src/core/config/schema.ts
@@ -8,7 +8,12 @@ import path from 'node:path';
import { pathToFileURL } from 'node:url';
import { BUNDLED_THEMES } from 'shiki';
import { z } from 'zod';
-import { appendForwardSlash, prependForwardSlash, trimSlashes } from '../path.js';
+import {
+ appendForwardSlash,
+ prependForwardSlash,
+ removeTrailingForwardSlash,
+ trimSlashes,
+} from '../path.js';
const ASTRO_CONFIG_DEFAULTS = {
root: '.',
@@ -366,22 +371,14 @@ export function createRelativeSchema(cmd: string, fileProtocolRoot: string) {
) {
config.build.client = new URL('./dist/client/', config.outDir);
}
- const trimmedBase = trimSlashes(config.base);
- // If there is no base but there is a base for site config, warn.
- const sitePathname = config.site && new URL(config.site).pathname;
- if (!trimmedBase.length && sitePathname && sitePathname !== '/') {
- config.base = sitePathname;
- /* eslint-disable no-console */
- console.warn(`The site configuration value includes a pathname of ${sitePathname} but there is no base configuration.
-
-A future version of Astro will stop using the site pathname when producing and