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
88export 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 - z A - Z 0 - 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 = / \. ( h t m l | h t m | c s s | j s | j s o n | x m l | t x t | p d f | j p g | j p e g | p n g | g i f | s v g | i c o | w o f f | w o f f 2 | t t f | e o t ) $ / 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