diff --git a/.changeset/fix-prerender-malformed-urls.md b/.changeset/fix-prerender-malformed-urls.md
new file mode 100644
index 000000000000..1b7b399d62fc
--- /dev/null
+++ b/.changeset/fix-prerender-malformed-urls.md
@@ -0,0 +1,5 @@
+---
+"@sveltejs/kit": patch
+---
+
+fix: prevent prerender memory issues with malformed URLs in meta tags
\ No newline at end of file
diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js
index bd815b37ba36..439bf1754bba 100644
--- a/packages/kit/src/core/postbuild/prerender.js
+++ b/packages/kit/src/core/postbuild/prerender.js
@@ -221,6 +221,26 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) {
const file = decoded.slice(config.paths.base.length + 1);
if (files.has(file)) return;
+ // Check for URLs that contain the prerender origin hostname in the path
+ // This can happen when meta tags reference URLs like "{page.url.host}/file.jpg"
+ // and can cause infinite loops during prerendering
+ if (config.prerender.origin !== 'http://sveltekit-prerender') {
+ // Extract hostname from prerender origin
+ const prerenderHost = new URL(config.prerender.origin).hostname;
+ // Check if the path starts with the prerender hostname (avoiding circular references)
+ if (decoded.startsWith(`/${prerenderHost}/`)) {
+ // This looks like a circular reference - handle as 404
+ handle_http_error({ status: 404, path: decoded, referrer, referenceType: 'linked' });
+ return;
+ }
+ } else {
+ // For the default sveltekit-prerender origin, check for the specific pattern
+ if (decoded.startsWith('/sveltekit-prerender/')) {
+ handle_http_error({ status: 404, path: decoded, referrer, referenceType: 'linked' });
+ return;
+ }
+ }
+
return q.add(() => visit(decoded, encoded || encodeURI(decoded), referrer, generated_from_id));
}
@@ -258,7 +278,13 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) {
}
// stuff in `static`
- return readFileSync(join(config.files.assets, file));
+ const static_path = join(config.files.assets, file);
+ // Check if the static file exists before trying to read it
+ // This prevents potential issues with malformed paths or non-existent files
+ if (!existsSync(static_path)) {
+ throw new Error(`Static file not found: ${file}`);
+ }
+ return readFileSync(static_path);
},
emulator
});
diff --git a/packages/kit/test/prerendering/basics/src/routes/circular-ref/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/circular-ref/+page.svelte
new file mode 100644
index 000000000000..9fd6dd27bd34
--- /dev/null
+++ b/packages/kit/test/prerendering/basics/src/routes/circular-ref/+page.svelte
@@ -0,0 +1,18 @@
+
+
+
This page tests that circular references in meta tags don't cause infinite loops
diff --git a/packages/kit/test/prerendering/basics/test/tests.spec.js b/packages/kit/test/prerendering/basics/test/tests.spec.js index e3972a1d369d..e12701b78b0f 100644 --- a/packages/kit/test/prerendering/basics/test/tests.spec.js +++ b/packages/kit/test/prerendering/basics/test/tests.spec.js @@ -286,3 +286,11 @@ test('identifies missing ids', () => { const missing_ids = JSON.parse(`[${missing_ids_content.slice(0, -1)}]`); expect(missing_ids).toEqual(['missing-id']); }); + +test('handles circular references in meta tags without infinite loops', () => { + // This test verifies that the circular-ref route was prerendered successfully + // without causing memory issues or infinite loops + const content = read('circular-ref.html'); + expect(content).toMatch('