Repository: sveltejs/kit (js)

Issue:
`App.PageError` should probably be renamed `App.Error`
### Describe the bug

...because it also applies to non-page requests (e.g. if you throw an error in `handle` or a `+server.js` file, it will go through `handleError`, and if it's a non-page request it will be turned into a JSON response).

Annoyingly, this _would_ be a breaking change

### Reproduction

N/A

### Logs

_No response_

### System Info

```shell
N/A
```


### Severity

annoyance

### Additional Information

_No response_

Pull request description:
rename `App.PageError` to `App.Error`
closes #6905. This is a breaking change, but I expect it to be very limited in impact.

### Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
- [x] It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
- [x] This message body should clearly illustrate what problems it solves.
- [ ] Ideally, include a test that fails without this PR but passes with it.

### Tests
- [x] Run the tests with `pnpm test` and lint the project with `pnpm lint` and `pnpm check`

### Changesets
- [x] If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running `pnpm changeset` and following the prompts. All changesets should be `patch` until SvelteKit 1.0


Patch:
diff --git a/.changeset/fifty-suns-hope.md b/.changeset/fifty-suns-hope.md
new file mode 100644
index 000000000..c261c2b40
--- /dev/null
+++ b/.changeset/fifty-suns-hope.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[fix] update page status when applying action
diff --git a/.changeset/great-tables-learn.md b/.changeset/great-tables-learn.md
new file mode 100644
index 000000000..8d6d60440
--- /dev/null
+++ b/.changeset/great-tables-learn.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Run `svelte-kit sync` in all workspace directories during postinstall
diff --git a/.changeset/hot-steaks-hang.md b/.changeset/hot-steaks-hang.md
new file mode 100644
index 000000000..e37477276
--- /dev/null
+++ b/.changeset/hot-steaks-hang.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Add origin header for non-GET and external requests made with server-side fetch
diff --git a/.changeset/kind-adults-drum.md b/.changeset/kind-adults-drum.md
new file mode 100644
index 000000000..43356b864
--- /dev/null
+++ b/.changeset/kind-adults-drum.md
@@ -0,0 +1,6 @@
+---
+'@sveltejs/kit': patch
+'create-svelte': patch
+---
+
+[breaking] rename App.PageError to App.Error
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 4eec432c0..43c489acb 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -313,6 +313,7 @@
     "few-walls-obey",
     "fifty-cougars-agree",
     "fifty-foxes-tan",
+    "fifty-suns-hope",
     "fifty-taxis-tease",
     "fifty-turtles-joke",
     "five-bags-prove",
@@ -428,6 +429,7 @@
     "great-lies-agree",
     "great-queens-rule",
     "great-seahorses-fry",
+    "great-tables-learn",
     "green-garlics-warn",
     "green-kiwis-roll",
     "green-masks-confess",
@@ -498,6 +500,7 @@
     "hot-lions-cough",
     "hot-mayflies-visit",
     "hot-planes-shout",
+    "hot-steaks-hang",
     "hot-taxis-jam",
     "hungry-coats-sort",
     "hungry-falcons-raise",
@@ -1041,6 +1044,7 @@
     "smart-peas-compare",
     "smart-planets-beam",
     "smooth-bags-sell",
+    "smooth-bears-unite",
     "smooth-cars-build",
     "smooth-cars-reflect",
     "smooth-comics-mate",
@@ -1162,6 +1166,7 @@
     "tasty-geese-burn",
     "tasty-geese-hug",
     "tasty-islands-drive",
+    "tasty-lizards-provide",
     "tasty-squids-share",
     "tasty-zebras-guess",
     "ten-apricots-admire",
diff --git a/.changeset/smooth-bears-unite.md b/.changeset/smooth-bears-unite.md
new file mode 100644
index 000000000..8659e746f
--- /dev/null
+++ b/.changeset/smooth-bears-unite.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[fix] apply redirect/error logic for `use:enhance`
diff --git a/.changeset/tasty-lizards-provide.md b/.changeset/tasty-lizards-provide.md
new file mode 100644
index 000000000..ce32d7dec
--- /dev/null
+++ b/.changeset/tasty-lizards-provide.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+chdir before attempting postinstall
diff --git a/documentation/docs/07-hooks.md b/documentation/docs/07-hooks.md
index 8ede315c3..c25f9b11a 100644
--- a/documentation/docs/07-hooks.md
+++ b/documentation/docs/07-hooks.md
@@ -136,14 +136,14 @@ The following can be added to `src/hooks.server.js` _and_ `src/hooks.client.js`:
 If an unexpected error is thrown during loading or rendering, this function will be called with the `error` and the `event`. This allows for two things:
 
 - you can log the error
-- you can generate a custom representation of the error that is safe to show to users, omitting sensitive details like messages and stack traces. The returned value becomes the value of `$page.error`. It defaults to `{ message: 'Not Found' }` in case of a 404 (you can detect them through `event.routeId` being `null`) and to `{ message: 'Internal Error' }` for everything else. To make this type-safe, you can customize the expected shape by declaring an `App.PageError` interface (which must include `message: string`, to guarantee sensible fallback behavior).
+- you can generate a custom representation of the error that is safe to show to users, omitting sensitive details like messages and stack traces. The returned value becomes the value of `$page.error`. It defaults to `{ message: 'Not Found' }` in case of a 404 (you can detect them through `event.routeId` being `null`) and to `{ message: 'Internal Error' }` for everything else. To make this type-safe, you can customize the expected shape by declaring an `App.Error` interface (which must include `message: string`, to guarantee sensible fallback behavior).
 
 The following code shows an example of typing the error shape as `{ message: string; code: string }` and returning it accordingly from the `handleError` functions:
 
 ```ts
 /// file: src/app.d.ts
 declare namespace App {
-	interface PageError {
+	interface Error {
 		message: string;
 		code: string;
 	}
diff --git a/packages/create-svelte/templates/default/src/app.d.ts b/packages/create-svelte/templates/default/src/app.d.ts
index 89fa05e74..f5675352f 100644
--- a/packages/create-svelte/templates/default/src/app.d.ts
+++ b/packages/create-svelte/templates/default/src/app.d.ts
@@ -8,7 +8,7 @@ declare namespace App {
 
 	// interface PageData {}
 
-	// interface PageError {}
+	// interface Error {}
 
 	// interface Platform {}
 }
diff --git a/packages/create-svelte/templates/skeleton/src/app.d.ts b/packages/create-svelte/templates/skeleton/src/app.d.ts
index 6f53deca8..8f4d63895 100644
--- a/packages/create-svelte/templates/skeleton/src/app.d.ts
+++ b/packages/create-svelte/templates/skeleton/src/app.d.ts
@@ -4,6 +4,6 @@
 declare namespace App {
 	// interface Locals {}
 	// interface PageData {}
-	// interface PageError {}
+	// interface Error {}
 	// interface Platform {}
 }
diff --git a/packages/create-svelte/templates/skeletonlib/src/app.d.ts b/packages/create-svelte/templates/skeletonlib/src/app.d.ts
index 2c2f9ec4f..20346fb23 100644
--- a/packages/create-svelte/templates/skeletonlib/src/app.d.ts
+++ b/packages/create-svelte/templates/skeletonlib/src/app.d.ts
@@ -6,6 +6,6 @@
 declare namespace App {
 	// interface Locals {}
 	// interface PageData {}
-	// interface PageError {}
+	// interface Error {}
 	// interface Platform {}
 }
diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md
index 1670fab7e..4ef0014e2 100644
--- a/packages/kit/CHANGELOG.md
+++ b/packages/kit/CHANGELOG.md
@@ -1,5 +1,31 @@
 # @sveltejs/kit
 
+## 1.0.0-next.498
+
+### Patch Changes
+
+- chdir before attempting postinstall ([#6969](https://github.com/sveltejs/kit/pull/6969))
+
+## 1.0.0-next.497
+
+### Patch Changes
+
+- Add origin header for non-GET and external requests made with server-side fetch ([#6901](https://github.com/sveltejs/kit/pull/6901))
+
+## 1.0.0-next.496
+
+### Patch Changes
+
+- [fix] update page status when applying action ([#6942](https://github.com/sveltejs/kit/pull/6942))
+
+* [fix] apply redirect/error logic for `use:enhance` ([#6828](https://github.com/sveltejs/kit/pull/6828))
+
+## 1.0.0-next.495
+
+### Patch Changes
+
+- Run `svelte-kit sync` in all workspace directories during postinstall ([#6949](https://github.com/sveltejs/kit/pull/6949))
+
 ## 1.0.0-next.494
 
 ### Patch Changes
diff --git a/packages/kit/package.json b/packages/kit/package.json
index 4d2ba27a3..18b4cf0d3 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/kit",
-	"version": "1.0.0-next.494",
+	"version": "1.0.0-next.498",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
@@ -54,6 +54,7 @@
 		"!src/core/**/test",
 		"types",
 		"svelte-kit.js",
+		"postinstall.js",
 		"scripts/special-types"
 	],
 	"scripts": {
@@ -67,7 +68,7 @@
 		"test:integration": "pnpm run -r --workspace-concurrency 1 --filter=\"./test/**\" test",
 		"test:unit": "uvu src \"(spec\\.js|test[\\\\/]index\\.js)\"",
 		"types": "node scripts/extract-types.js",
-		"postinstall": "node svelte-kit.js sync"
+		"postinstall": "node postinstall.js"
 	},
 	"exports": {
 		"./package.json": "./package.json",
diff --git a/packages/kit/postinstall.js b/packages/kit/postinstall.js
new file mode 100644
index 000000000..398eb37cd
--- /dev/null
+++ b/packages/kit/postinstall.js
@@ -0,0 +1,38 @@
+import fs from 'fs';
+import path from 'path';
+import glob from 'tiny-glob/sync.js';
+import { load_config } from './src/core/config/index.js';
+import * as sync from './src/core/sync/sync.js';
+
+const cwd = process.env.INIT_CWD ?? process.cwd();
+process.chdir(cwd);
+
+const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
+
+const directories = [];
+
+if (pkg.workspaces) {
+	for (const directory of pkg.workspaces) {
+		directories.push(...glob(directory, { cwd }).map((dir) => path.resolve(cwd, dir)));
+	}
+} else {
+	directories.push(cwd);
+}
+
+for (const cwd of directories) {
+	process.chdir(cwd);
+
+	if (!fs.existsSync('package.json')) continue;
+	if (!fs.existsSync('svelte.config.js')) continue;
+
+	const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
+	if (!pkg.dependencies?.['@sveltejs/kit'] && !pkg.devDependencies?.['@sveltejs/kit']) continue;
+
+	try {
+		const config = await load_config();
+		await sync.all(config, 'development');
+	} catch (e) {
+		console.log('Error while trying to sync SvelteKit config');
+		console.log(e);
+	}
+}
diff --git a/packages/kit/src/cli.js b/packages/kit/src/cli.js
index 98348e6f0..6e931aaf9 100755
--- a/packages/kit/src/cli.js
+++ b/packages/kit/src/cli.js
@@ -41,10 +41,6 @@ prog
 			return;
 		}
 
-		if (event === 'postinstall' && process.env.INIT_CWD) {
-			process.chdir(process.env.INIT_CWD);
-		}
-
 		if (!fs.existsSync('svelte.config.js')) {
 			console.warn(`Missing ${path.resolve('svelte.config.js')} — skipping`);
 			return;
diff --git a/packages/kit/src/core/sync/sync.js b/packages/kit/src/core/sync/sync.js
index 5949de8fa..ce7c8dccf 100644
--- a/packages/kit/src/core/sync/sync.js
+++ b/packages/kit/src/core/sync/sync.js
@@ -1,3 +1,4 @@
+import fs from 'fs';
 import path from 'path';
 import create_manifest_data from './create_manifest_data/index.js';
 import { write_client_manifest } from './write_client_manifest.js';
@@ -13,6 +14,16 @@ import { write_ambient } from './write_ambient.js';
  * @param {string} mode
  */
 export function init(config, mode) {
+	// TODO remove for 1.0
+	if (fs.existsSync('src/app.d.ts')) {
+		const content = fs.readFileSync('src/app.d.ts', 'utf-8');
+		if (content.includes('PageError')) {
+			throw new Error(
+				'App.PageError has been renamed to App.Error — please update your src/app.d.ts'
+			);
+		}
+	}
+
 	write_tsconfig(config.kit);
 	write_ambient(config.kit, mode);
 }
diff --git a/packages/kit/src/runtime/app/forms.js b/packages/kit/src/runtime/app/forms.js
index 6ab589caa..bf0601385 100644
--- a/packages/kit/src/runtime/app/forms.js
+++ b/packages/kit/src/runtime/app/forms.js
@@ -28,7 +28,13 @@ export function enhance(form, submit = () => {}) {
 			await invalidateAll();
 		}
 
-		if (location.origin + location.pathname === action.origin + action.pathname) {
+		// For success/invalid results, only apply action if it belongs to the
+		// current page, otherwise `form` will be updated erroneously
+		if (
+			location.origin + location.pathname === action.origin + action.pathname ||
+			result.type === 'redirect' ||
+			result.type === 'error'
+		) {
 			applyAction(result);
 		}
 	};
diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js
index 071423a6f..397e4daa1 100644
--- a/packages/kit/src/runtime/client/client.js
+++ b/packages/kit/src/runtime/client/client.js
@@ -395,7 +395,7 @@ export function create_client({ target, base, trailing_slash }) {
 	 *   params: Record<string, string>;
 	 *   branch: Array<import('./types').BranchNode | undefined>;
 	 *   status: number;
-	 *   error: App.PageError | null;
+	 *   error: App.Error | null;
 	 *   route: import('types').CSRRoute | null;
 	 *   form?: Record<string, any> | null;
 	 * }} opts
@@ -799,7 +799,7 @@ export function create_client({ target, base, trailing_slash }) {
 					}
 
 					let status = 500;
-					/** @type {App.PageError} */
+					/** @type {App.Error} */
 					let error;
 
 					if (server_data_nodes?.includes(/** @type {import('types').ServerErrorNode} */ (err))) {
@@ -814,40 +814,22 @@ export function create_client({ target, base, trailing_slash }) {
 						error = handle_error(err, { params, url, routeId: route.id });
 					}
 
-					while (i--) {
-						if (errors[i]) {
-							/** @type {import('./types').BranchNode | undefined} */
-							let error_loaded;
-
-							let j = i;
-							while (!branch[j]) j -= 1;
-							try {
-								error_loaded = {
-									node: await /** @type {import('types').CSRPageNodeLoader } */ (errors[i])(),
-									loader: /** @type {import('types').CSRPageNodeLoader } */ (errors[i]),
-									data: {},
-									server: null,
-									shared: null
-								};
-
-								return await get_navigation_result_from_branch({
-									url,
-									params,
-									branch: branch.slice(0, j + 1).concat(error_loaded),
-									status,
-									error,
-									route
-								});
-							} catch (e) {
-								continue;
-							}
-						}
+					const error_load = await load_nearest_error_page(i, branch, errors);
+					if (error_load) {
+						return await get_navigation_result_from_branch({
+							url,
+							params,
+							branch: branch.slice(0, error_load.idx).concat(error_load.node),
+							status,
+							error,
+							route
+						});
+					} else {
+						// if we get here, it's because the root `load` function failed,
+						// and we need to fall back to the server
+						await native_navigation(url);
+						return;
 					}
-
-					// if we get here, it's because the root `load` function failed,
-					// and we need to fall back to the server
-					await native_navigation(url);
-					return;
 				}
 			} else {
 				// push an empty slot so we can rewind past gaps to the
@@ -868,6 +850,35 @@ export function create_client({ target, base, trailing_slash }) {
 		});
 	}
 
+	/**
+	 * @param {number} i Start index to backtrack from
+	 * @param {Array<import('./types').BranchNode | undefined>} branch Branch to backtrack
+	 * @param {Array<import('types').CSRPageNodeLoader | undefined>} errors All error pages for this branch
+	 * @returns {Promise<{idx: number; node: import('./types').BranchNode} | undefined>}
+	 */
+	async function load_nearest_error_page(i, branch, errors) {
+		while (i--) {
+			if (errors[i]) {
+				let j = i;
+				while (!branch[j]) j -= 1;
+				try {
+					return {
+						idx: j + 1,
+						node: {
+							node: await /** @type {import('types').CSRPageNodeLoader } */ (errors[i])(),
+							loader: /** @type {import('types').CSRPageNodeLoader } */ (errors[i]),
+							data: {},
+							server: null,
+							shared: null
+						}
+					};
+				} catch (e) {
+					continue;
+				}
+			}
+		}
+	}
+
 	/**
 	 * @param {{
 	 *   status: number;
@@ -1160,44 +1171,26 @@ export function create_client({ target, base, trailing_slash }) {
 				const { branch, route } = current;
 				if (!route) return;
 
-				let i = current.branch.length;
-
-				while (i--) {
-					if (route.errors[i]) {
-						/** @type {import('./types').BranchNode | undefined} */
-						let error_loaded;
-
-						let j = i;
-						while (!branch[j]) j -= 1;
-						try {
-							error_loaded = {
-								node: await /** @type {import('types').CSRPageNodeLoader } */ (route.errors[i])(),
-								loader: /** @type {import('types').CSRPageNodeLoader } */ (route.errors[i]),
-								data: {},
-								server: null,
-								shared: null
-							};
-
-							const navigation_result = await get_navigation_result_from_branch({
-								url,
-								params: current.params,
-								branch: branch.slice(0, j + 1).concat(error_loaded),
-								status: 500, // TODO might not be 500?
-								error: result.error,
-								route
-							});
-
-							current = navigation_result.state;
-
-							const post_update = pre_update();
-							root.$set(navigation_result.props);
-							post_update();
-
-							return;
-						} catch (e) {
-							continue;
-						}
-					}
+				const error_load = await load_nearest_error_page(
+					current.branch.length,
+					branch,
+					route.errors
+				);
+				if (error_load) {
+					const navigation_result = await get_navigation_result_from_branch({
+						url,
+						params: current.params,
+						branch: branch.slice(0, error_load.idx).concat(error_load.node),
+						status: 500, // TODO might not be 500?
+						error: result.error,
+						route
+					});
+
+					current = navigation_result.state;
+
+					const post_update = pre_update();
+					root.$set(navigation_result.props);
+					post_update();
 				}
 			} else if (result.type === 'redirect') {
 				goto(result.location, {}, []);
@@ -1206,10 +1199,8 @@ export function create_client({ target, base, trailing_slash }) {
 				const props = { form: result.data };
 
 				if (result.status !== page.status) {
-					props.page = {
-						...page,
-						status: result.status
-					};
+					page = { ...page, status: result.status };
+					props.page = page;
 				}
 
 				const post_update = pre_update();
@@ -1513,7 +1504,7 @@ async function load_data(url, invalid) {
 /**
  * @param {unknown} error
  * @param {import('types').NavigationEvent} event
- * @returns {App.PageError}
+ * @returns {App.Error}
  */
 function handle_error(error, event) {
 	return (
diff --git a/packages/kit/src/runtime/client/types.d.ts b/packages/kit/src/runtime/client/types.d.ts
index 4e88d9132..3b441ec45 100644
--- a/packages/kit/src/runtime/client/types.d.ts
+++ b/packages/kit/src/runtime/client/types.d.ts
@@ -25,7 +25,7 @@ export interface Client {
 	// private API
 	_hydrate: (opts: {
 		status: number;
-		error: App.PageError;
+		error: App.Error;
 		node_ids: number[];
 		params: Record<string, string>;
 		routeId: string | null;
@@ -77,7 +77,7 @@ export interface DataNode {
 
 export interface NavigationState {
 	branch: Array<BranchNode | undefined>;
-	error: App.PageError | null;
+	error: App.Error | null;
 	params: Record<string, string>;
 	route: CSRRoute | null;
 	url: URL;
diff --git a/packages/kit/src/runtime/control.js b/packages/kit/src/runtime/control.js
index ba2e9db90..bd6c5d499 100644
--- a/packages/kit/src/runtime/control.js
+++ b/packages/kit/src/runtime/control.js
@@ -1,7 +1,7 @@
 export class HttpError {
 	/**
 	 * @param {number} status
-	 * @param {{message: string} extends App.PageError ? (App.PageError | string | undefined) : App.PageError} body
+	 * @param {{message: string} extends App.Error ? (App.Error | string | undefined) : App.Error} body
 	 */
 	constructor(status, body) {
 		this.status = status;
diff --git a/packages/kit/src/runtime/server/page/fetch.js b/packages/kit/src/runtime/server/page/fetch.js
index fdd0a4658..d84372930 100644
--- a/packages/kit/src/runtime/server/page/fetch.js
+++ b/packages/kit/src/runtime/server/page/fetch.js
@@ -68,6 +68,19 @@ export function create_fetch({ event, options, state, route, prerender_default,
 
 				const url = new URL(request.url);
 
+				if (!request.headers.has('origin')) {
+					request.headers.set('origin', event.url.origin);
+				}
+
+				// Remove Origin, according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin#description
+				if (
+					(request.method === 'GET' || request.method === 'HEAD') &&
+					((request.mode === 'no-cors' && url.origin !== event.url.origin) ||
+						url.origin === event.url.origin)
+				) {
+					request.headers.delete('origin');
+				}
+
 				if (url.origin !== event.url.origin) {
 					// allow cookie passthrough for "same-origin"
 					// if SvelteKit is serving my.domain.com:
diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js
index 2bb13b92d..66fb873b7 100644
--- a/packages/kit/src/runtime/server/page/render.js
+++ b/packages/kit/src/runtime/server/page/render.js
@@ -23,7 +23,7 @@ const updated = {
  *   state: import('types').SSRState;
  *   page_config: { ssr: boolean; csr: boolean };
  *   status: number;
- *   error: App.PageError | null;
+ *   error: App.Error | null;
  *   event: import('types').RequestEvent;
  *   resolve_opts: import('types').RequiredResolveOptions;
  *   action_result?: import('types').ActionResult;
diff --git a/packages/kit/src/runtime/server/utils.js b/packages/kit/src/runtime/server/utils.js
index c137f9f4f..00bb1e4d9 100644
--- a/packages/kit/src/runtime/server/utils.js
+++ b/packages/kit/src/runtime/server/utils.js
@@ -154,7 +154,7 @@ export function handle_fatal_error(event, options, error) {
  * @param {import('types').RequestEvent} event
  * @param {import('types').SSROptions} options
  * @param {any} error
- * @returns {App.PageError}
+ * @returns {App.Error}
  */
 export function handle_error_and_jsonify(event, options, error) {
 	if (error instanceof HttpError) {
diff --git a/packages/kit/types/ambient.d.ts b/packages/kit/types/ambient.d.ts
index 026ce6c5e..2405fa7ac 100644
--- a/packages/kit/types/ambient.d.ts
+++ b/packages/kit/types/ambient.d.ts
@@ -40,6 +40,13 @@
  *
  */
 declare namespace App {
+	/**
+	 * Defines the common shape of expected and unexpected errors. Expected errors are thrown using the `error` function. Unexpected errors are handled by the `handleError` hooks which should return this shape.
+	 */
+	export interface Error {
+		message: string;
+	}
+
 	/**
 	 * The interface that defines `event.locals`, which can be accessed in [hooks](https://kit.svelte.dev/docs/hooks) (`handle`, and `handleError`), server-only `load` functions, and `+server.js` files.
 	 */
@@ -56,13 +63,6 @@ declare namespace App {
 	 * If your adapter provides [platform-specific context](https://kit.svelte.dev/docs/adapters#supported-environments-platform-specific-context) via `event.platform`, you can specify it here.
 	 */
 	export interface Platform {}
-
-	/**
-	 * Defines the common shape of expected and unexpected errors. Expected errors are thrown using the `error` function. Unexpected errors are handled by the `handleError` hooks which should return this shape.
-	 */
-	export interface PageError {
-		message: string;
-	}
 }
 
 /**
diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts
index c48dba8ad..fcf925ca1 100644
--- a/packages/kit/types/index.d.ts
+++ b/packages/kit/types/index.d.ts
@@ -236,11 +236,11 @@ export interface Handle {
 }
 
 export interface HandleServerError {
-	(input: { error: unknown; event: RequestEvent }): void | App.PageError;
+	(input: { error: unknown; event: RequestEvent }): void | App.Error;
 }
 
 export interface HandleClientError {
-	(input: { error: unknown; event: NavigationEvent }): void | App.PageError;
+	(input: { error: unknown; event: NavigationEvent }): void | App.Error;
 }
 
 export interface HandleFetch {
@@ -300,7 +300,7 @@ export interface Page<Params extends Record<string, string> = Record<string, str
 	params: Params;
 	routeId: string | null;
 	status: number;
-	error: App.PageError | null;
+	error: App.Error | null;
 	data: App.PageData & Record<string, any>;
 }
 
@@ -415,13 +415,13 @@ export type ActionResult<
  * This object, if thrown during request handling, will cause SvelteKit to
  * return an error response without invoking `handleError`
  * @param status The HTTP status code
- * @param body An object that conforms to the App.PageError type. If a string is passed, it will be used as the message property.
+ * @param body An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
  */
-export function error(status: number, body: App.PageError): HttpError;
+export function error(status: number, body: App.Error): HttpError;
 export function error(
 	status: number,
-	// this overload ensures you can omit the argument or pass in a string if App.PageError is of type { message: string }
-	body?: { message: string } extends App.PageError ? App.PageError | string | undefined : never
+	// this overload ensures you can omit the argument or pass in a string if App.Error is of type { message: string }
+	body?: { message: string } extends App.Error ? App.Error | string | undefined : never
 ): HttpError;
 
 /**
diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts
index 29f84240d..3f7ff7d76 100644
--- a/packages/kit/types/internal.d.ts
+++ b/packages/kit/types/internal.d.ts
@@ -226,7 +226,7 @@ export interface ServerDataSkippedNode {
  */
 export interface ServerErrorNode {
 	type: 'error';
-	error: App.PageError;
+	error: App.Error;
 	/**
 	 * Only set for HttpErrors
 	 */
@@ -288,7 +288,7 @@ export interface SSROptions {
 		check_origin: boolean;
 	};
 	dev: boolean;
-	handle_error(error: Error & { frame?: string }, event: RequestEvent): App.PageError;
+	handle_error(error: Error & { frame?: string }, event: RequestEvent): App.Error;
 	hooks: ServerHooks;
 	manifest: SSRManifest;
 	paths: {


Test patch:
diff --git a/packages/kit/test/apps/basics/src/routes/actions/redirect/+page.server.js b/packages/kit/test/apps/basics/src/routes/actions/redirect/+page.server.js
new file mode 100644
index 000000000..ff16ad14f
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/actions/redirect/+page.server.js
@@ -0,0 +1,15 @@
+import { redirect } from '@sveltejs/kit';
+
+/** @type {import('./$types').PageServerLoad} */
+export function load() {
+	return {
+		initial: 'initial'
+	};
+}
+
+/** @type {import('./$types').Actions} */
+export const actions = {
+	default: async () => {
+		throw redirect(303, '/actions/enhance');
+	}
+};
diff --git a/packages/kit/test/apps/basics/src/routes/actions/redirect/+page.svelte b/packages/kit/test/apps/basics/src/routes/actions/redirect/+page.svelte
new file mode 100644
index 000000000..7bcb55f72
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/actions/redirect/+page.svelte
@@ -0,0 +1,7 @@
+<script>
+	import { enhance } from '$app/forms';
+</script>
+
+<form method="post" use:enhance>
+	<button>Submit</button>
+</form>
diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-origin-external/+page.js b/packages/kit/test/apps/basics/src/routes/load/fetch-origin-external/+page.js
new file mode 100644
index 000000000..35b8df449
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/fetch-origin-external/+page.js
@@ -0,0 +1,8 @@
+/** @type {import('./$types').PageLoad} */
+export async function load({ fetch, url }) {
+	const port = url.searchParams.get('port');
+	const res = await fetch(`http://localhost:${port}`);
+
+	const { origin } = await res.json();
+	return { origin };
+}
diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-origin-external/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/fetch-origin-external/+page.svelte
new file mode 100644
index 000000000..aa194cc01
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/fetch-origin-external/+page.svelte
@@ -0,0 +1,6 @@
+<script>
+	/** @type {import('./$types').PageData} */
+	export let data;
+</script>
+
+<h1>origin: {data.origin}</h1>
diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-origin-internal/+page.js b/packages/kit/test/apps/basics/src/routes/load/fetch-origin-internal/+page.js
new file mode 100644
index 000000000..a8a830652
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/fetch-origin-internal/+page.js
@@ -0,0 +1,10 @@
+/** @type {import('./$types').PageLoad} */
+export async function load({ fetch }) {
+	const res = await fetch('/load/fetch-origin-internal/resource', {
+		method: 'POST'
+	});
+
+	const { origin } = await res.json();
+
+	return { origin };
+}
diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-origin-internal/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/fetch-origin-internal/+page.svelte
new file mode 100644
index 000000000..aa194cc01
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/fetch-origin-internal/+page.svelte
@@ -0,0 +1,6 @@
+<script>
+	/** @type {import('./$types').PageData} */
+	export let data;
+</script>
+
+<h1>origin: {data.origin}</h1>
diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-origin-internal/resource/+server.js b/packages/kit/test/apps/basics/src/routes/load/fetch-origin-internal/resource/+server.js
new file mode 100644
index 000000000..ad1c6605f
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/fetch-origin-internal/resource/+server.js
@@ -0,0 +1,7 @@
+import { json } from '@sveltejs/kit';
+
+/** @type {import('./$types').RequestHandler} */
+export function POST({ request }) {
+	const origin = request.headers.get('origin');
+	return json({ origin });
+}
diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js
index 59f1c00ec..2ea3737df 100644
--- a/packages/kit/test/apps/basics/test/server.test.js
+++ b/packages/kit/test/apps/basics/test/server.test.js
@@ -272,6 +272,32 @@ test.describe('Load', () => {
 		const response = await request.get('/load/static-file-with-hash');
 		expect(await response.text()).toContain('status: 404');
 	});
+
+	test('includes origin header on non-GET internal request', async ({ page, baseURL }) => {
+		await page.goto('/load/fetch-origin-internal');
+		expect(await page.textContent('h1')).toBe(`origin: ${new URL(baseURL).origin}`);
+	});
+
+	test('includes origin header on external request', async ({ page, baseURL }) => {
+		const { port, close } = await start_server((req, res) => {
+			if (req.url === '/') {
+				res.writeHead(200, {
+					'content-type': 'application/json',
+					'access-control-allow-origin': '*'
+				});
+
+				res.end(JSON.stringify({ origin: req.headers.origin }));
+			} else {
+				res.writeHead(404);
+				res.end('not found');
+			}
+		});
+
+		await page.goto(`/load/fetch-origin-external?port=${port}`);
+		expect(await page.textContent('h1')).toBe(`origin: ${new URL(baseURL).origin}`);
+
+		close();
+	});
 });
 
 test.describe('Routing', () => {
diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js
index e63236b8b..9332b0825 100644
--- a/packages/kit/test/apps/basics/test/test.js
+++ b/packages/kit/test/apps/basics/test/test.js
@@ -1816,7 +1816,7 @@ test.describe('Actions', () => {
 		}
 	});
 
-	test('use:enhance', async ({ page, app }) => {
+	test('use:enhance', async ({ page }) => {
 		await page.goto('/actions/enhance');
 
 		expect(await page.textContent('pre')).toBe(JSON.stringify(null));
@@ -1860,6 +1860,17 @@ test.describe('Actions', () => {
 
 		await expect(page.locator('pre')).toHaveText(JSON.stringify({ result: 'register: foo' }));
 	});
+
+	test('redirect', async ({ page }) => {
+		await page.goto('/actions/redirect');
+
+		page.click('button');
+
+		await page.waitForResponse('/actions/redirect');
+		await page.waitForTimeout(50);
+
+		expect(page.url()).toContain('/actions/enhance');
+	});
 });
 
 test.describe('Cookies API', () => {