Skip to content

Commit 49c1cc8

Browse files
authored
Improve efficiency of Cloudflare redirect edge function (#1635)
Improves on the fix deployed in #1631 so that the function fires only when the URL is missing the trailing slash and would result in a 404. Should reduce the likelihood of hitting Cloudflare rate limiting, while slightly increasing the likelihood of getting an obscure 404 if it is not in one of the top level directories we are considering.
1 parent 54c48af commit 49c1cc8

1 file changed

Lines changed: 45 additions & 76 deletions

File tree

functions/_middleware.js

Lines changed: 45 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,64 @@
11
/**
22
* Cloudflare Pages middleware to handle trailing slash redirects
33
*
4-
* This middleware addresses the known Cloudflare Pages issue where URLs without
5-
* trailing slashes don't match redirect rules that include trailing slashes.
4+
* HIGHLY OPTIMIZED: Minimal processing to avoid rate limits
5+
* Only checks specific patterns known to have redirect issues
66
*/
77

88
export async function onRequest(context) {
99
const { request, env, next } = context;
10-
const url = new URL(request.url);
11-
12-
// Only process GET and HEAD requests
10+
11+
// OPTIMIZATION: Only check GET/HEAD requests
1312
if (request.method !== 'GET' && request.method !== 'HEAD') {
1413
return next();
1514
}
16-
17-
// Skip if the path already has a trailing slash
18-
if (url.pathname.endsWith('/')) {
15+
16+
const url = new URL(request.url);
17+
const pathname = url.pathname;
18+
19+
// OPTIMIZATION: Skip if already has trailing slash
20+
if (pathname.endsWith('/')) {
1921
return next();
2022
}
21-
22-
// Skip if the path has a file extension
23-
const hasFileExtension = /\.[a-zA-Z0-9]+$/.test(url.pathname);
24-
if (hasFileExtension) {
23+
24+
// OPTIMIZATION: Skip if has file extension (quick check)
25+
if (pathname.includes('.')) {
2526
return next();
2627
}
27-
28-
// Skip certain system paths
29-
const skipPaths = ['/robots.txt', '/sitemap.xml', '/_redirects', '/favicon.ico'];
30-
if (skipPaths.includes(url.pathname)) {
28+
29+
// OPTIMIZATION: Only process paths with 2+ segments
30+
// Single segment paths like /about rarely need this fix
31+
const segments = pathname.split('/').filter(s => s);
32+
if (segments.length < 2) {
3133
return next();
3234
}
33-
34-
// Skip paths with dots that might not be file extensions
35-
// but could be version numbers or other valid path segments
36-
// Only skip if the dot is followed by common file extensions
37-
const commonExtensions = /\.(html|htm|css|js|json|xml|txt|pdf|jpg|jpeg|png|gif|svg|ico|woff|woff2|ttf|eot)$/i;
38-
if (commonExtensions.test(url.pathname)) {
35+
36+
// OPTIMIZATION: Only check paths that match common redirect patterns
37+
// Add more patterns here as needed based on your _redirects file
38+
const needsCheck =
39+
pathname.startsWith('/wandb/') ||
40+
pathname.startsWith('/library/') ||
41+
pathname.startsWith('/guides/') ||
42+
pathname.startsWith('/ref/') ||
43+
pathname.startsWith('/sweeps/') ||
44+
pathname.startsWith('/artifacts/') ||
45+
pathname.startsWith('/frameworks/') ||
46+
pathname.startsWith('/company/') ||
47+
pathname.startsWith('/tutorials/');
48+
49+
if (!needsCheck) {
3950
return next();
4051
}
41-
42-
// Create URL with trailing slash, preserving query and hash
43-
const urlWithSlash = new URL(request.url);
44-
urlWithSlash.pathname = url.pathname + '/';
45-
46-
// Create a new request with the trailing slash
47-
const requestWithSlash = new Request(urlWithSlash.toString(), {
48-
method: request.method,
49-
headers: request.headers,
50-
redirect: 'manual' // Important: don't follow redirects
51-
});
52-
53-
try {
54-
// Fetch with the trailing slash URL
55-
const response = await env.ASSETS.fetch(requestWithSlash);
56-
57-
// If we get a redirect response (301, 302, etc.), redirect to URL with slash
58-
if (response.status >= 301 && response.status <= 308) {
59-
// The path with trailing slash matches a redirect rule
60-
// Redirect the browser to the URL with slash, which will then trigger the actual redirect
61-
// Preserve query parameters and hash
62-
const redirectUrl = new URL(request.url);
63-
redirectUrl.pathname = url.pathname + '/';
64-
65-
return new Response(null, {
66-
status: 301,
67-
headers: {
68-
'Location': redirectUrl.pathname + redirectUrl.search + redirectUrl.hash,
69-
'Cache-Control': 'public, max-age=3600'
70-
}
71-
});
72-
}
73-
74-
// If the response is successful (200), it means the trailing slash version exists
75-
// In this case, we should also redirect to maintain consistency
76-
if (response.status === 200) {
77-
const redirectUrl = new URL(request.url);
78-
redirectUrl.pathname = url.pathname + '/';
79-
80-
return new Response(null, {
81-
status: 301,
82-
headers: {
83-
'Location': redirectUrl.pathname + redirectUrl.search + redirectUrl.hash,
84-
'Cache-Control': 'public, max-age=3600'
85-
}
86-
});
52+
53+
// At this point, we have a high-probability redirect candidate
54+
// Redirect to path with trailing slash
55+
const redirectUrl = pathname + '/' + url.search + url.hash;
56+
57+
return new Response(null, {
58+
status: 301,
59+
headers: {
60+
'Location': redirectUrl,
61+
'Cache-Control': 'public, max-age=3600'
8762
}
88-
} catch (error) {
89-
// Log error but continue with original request
90-
console.error('Trailing slash middleware error:', error);
91-
}
92-
93-
// No redirect found, continue with the original request
94-
return next();
95-
}
63+
});
64+
}

0 commit comments

Comments
 (0)