Skip to content

Commit 92b4764

Browse files
committed
ignore redirects in proxy or nonapi requests
1 parent d26674e commit 92b4764

1 file changed

Lines changed: 127 additions & 109 deletions

File tree

src/index.ts

Lines changed: 127 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,141 @@
1-
/**
2-
* Cloudflare Workers Reverse Proxy with Cookie Handling
3-
*
4-
* This worker proxies requests to a backend API while maintaining cookies and handling CORS.
5-
*/
6-
export default {
7-
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
8-
const url = new URL(request.url);
1+
// src/index.ts
2+
export interface Env {
3+
// Add your environment variables if needed
4+
}
95

10-
// Determine if this is an API request that should be proxied
11-
if (url.pathname.startsWith('/fly-api/')) {
12-
return await handleApiRequest(request, env, url);
13-
}
6+
// Export the handleRequest function for Pages Functions
7+
export const handleRequest = async (request: Request, env: Env, ctx: ExecutionContext): Promise<Response> => {
8+
const url = new URL(request.url);
149

15-
// For non-API requests, return a simple response or redirect
16-
return new Response('This is a proxy worker. API requests should be sent to /fly-api/...');
17-
},
18-
} satisfies ExportedHandler<Env>;
10+
// Determine if this is an API request that should be proxied
11+
if (url.pathname.startsWith('/fly-api/')) {
12+
return await handleApiRequest(request, env, url);
13+
}
14+
15+
// default behavior for non-API requests, next()
16+
// return env.ASSETS.fetch(request);
17+
return fetch(request);
18+
};
1919

2020
/**
2121
* Handles API requests by proxying them to the backend
2222
*/
2323
async function handleApiRequest(request: Request, env: Env, url: URL): Promise<Response> {
24-
const backendUrl = 'https://laclipasa-backend.fly.dev' + url.pathname.replace('/fly-api', '') + url.search;
25-
26-
const headers = new Headers(request.headers);
27-
28-
const modifiedRequest = new Request(backendUrl, {
29-
method: request.method,
30-
headers: headers,
31-
body: request.body,
32-
redirect: 'follow'
33-
});
34-
35-
const response = await fetch(modifiedRequest);
36-
37-
const responseData = await response.arrayBuffer();
38-
39-
const newResponse = new Response(responseData, {
40-
status: response.status,
41-
statusText: response.statusText,
42-
});
43-
44-
response.headers.forEach((value, key) => {
45-
if (key.toLowerCase() !== 'set-cookie') {
46-
newResponse.headers.set(key, value);
47-
}
48-
});
49-
50-
const setCookieHeaders = response.headers.getAll('Set-Cookie');
51-
if (setCookieHeaders && setCookieHeaders.length > 0) {
52-
setCookieHeaders.forEach(cookie => {
53-
const modifiedCookie = modifyCookie(cookie, request.url);
54-
if (modifiedCookie) {
55-
newResponse.headers.append('Set-Cookie', modifiedCookie);
56-
}
57-
});
58-
}
59-
60-
// Add CORS headers to allow cross-domain requests
61-
newResponse.headers.set('Access-Control-Allow-Origin', url.origin);
62-
newResponse.headers.set('Access-Control-Allow-Credentials', 'true');
63-
newResponse.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
64-
newResponse.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
65-
66-
return newResponse;
24+
const backendUrl = 'https://laclipasa-backend.fly.dev' + url.pathname.replace('/fly-api', '') + url.search;
25+
26+
const headers = new Headers(request.headers);
27+
28+
const modifiedRequest = new Request(backendUrl, {
29+
method: request.method,
30+
headers: headers,
31+
body: request.body,
32+
redirect: 'manual' // Changed from 'follow' to 'manual' to handle redirects ourselves
33+
});
34+
35+
const response = await fetch(modifiedRequest);
36+
37+
// Check if response is a redirect (status codes 301, 302, 303, 307, 308)
38+
if (response.status >= 300 && response.status < 400 && response.headers.has('Location')) {
39+
// For redirects, pass through the response without modification
40+
// This allows the browser to handle the redirect properly
41+
const redirectLocation = response.headers.get('Location');
42+
43+
return new Response(null, {
44+
status: response.status,
45+
statusText: response.statusText,
46+
headers: response.headers
47+
});
48+
}
49+
50+
const responseData = await response.arrayBuffer();
51+
52+
const newResponse = new Response(responseData, {
53+
status: response.status,
54+
statusText: response.statusText,
55+
});
56+
57+
response.headers.forEach((value, key) => {
58+
if (key.toLowerCase() !== 'set-cookie') {
59+
newResponse.headers.set(key, value);
60+
}
61+
});
62+
63+
const setCookieHeaders = response.headers.getAll('Set-Cookie');
64+
if (setCookieHeaders && setCookieHeaders.length > 0) {
65+
setCookieHeaders.forEach(cookie => {
66+
const modifiedCookie = modifyCookie(cookie, request.url);
67+
if (modifiedCookie) {
68+
newResponse.headers.append('Set-Cookie', modifiedCookie);
69+
}
70+
});
71+
}
72+
73+
// Add CORS headers to allow cross-domain requests
74+
newResponse.headers.set('Access-Control-Allow-Origin', url.origin);
75+
newResponse.headers.set('Access-Control-Allow-Credentials', 'true');
76+
newResponse.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
77+
newResponse.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
78+
79+
return newResponse;
6780
}
6881

6982
/**
7083
* Modifies cookies to ensure they work correctly across domains
7184
*/
7285
function modifyCookie(cookie: string, requestUrl: string): string | null {
73-
if (!cookie) return null;
74-
75-
const url = new URL(requestUrl);
76-
const cookieParts = cookie.split(';').map(part => part.trim());
77-
const mainPart = cookieParts[0]; // This contains name=value
78-
79-
// Create a new array for the modified cookie parts
80-
const newCookieParts = [mainPart];
81-
82-
// Keep track if we've seen these attributes
83-
let hasDomain = false;
84-
let hasSameSite = false;
85-
let hasSecure = false;
86-
87-
// Process all cookie attributes except the main part
88-
for (let i = 1; i < cookieParts.length; i++) {
89-
const part = cookieParts[i].toLowerCase();
90-
91-
// Check for existing attributes
92-
if (part.startsWith('domain=')) {
93-
// Replace domain with the domain from the request URL
94-
newCookieParts.push(`Domain=${url.hostname}`);
95-
hasDomain = true;
96-
} else if (part.startsWith('samesite=')) {
97-
// Keep original SameSite or set to None for cross-domain cookies
98-
newCookieParts.push('SameSite=None');
99-
hasSameSite = true;
100-
} else if (part === 'secure') {
101-
newCookieParts.push('Secure');
102-
hasSecure = true;
103-
} else {
104-
// Keep all other attributes as they are
105-
newCookieParts.push(cookieParts[i]);
106-
}
107-
}
108-
109-
// Add missing attributes if needed
110-
if (!hasDomain) {
111-
newCookieParts.push(`Domain=${url.hostname}`);
112-
}
113-
114-
if (!hasSameSite) {
115-
newCookieParts.push('SameSite=None');
116-
}
117-
118-
if (!hasSecure) {
119-
newCookieParts.push('Secure');
120-
}
121-
122-
return newCookieParts.join('; ');
86+
if (!cookie) return null;
87+
88+
const url = new URL(requestUrl);
89+
const cookieParts = cookie.split(';').map(part => part.trim());
90+
const mainPart = cookieParts[0]; // This contains name=value
91+
92+
// Create a new array for the modified cookie parts
93+
const newCookieParts = [mainPart];
94+
95+
// Keep track if we've seen these attributes
96+
let hasDomain = false;
97+
let hasSameSite = false;
98+
let hasSecure = false;
99+
100+
// Process all cookie attributes except the main part
101+
for (let i = 1; i < cookieParts.length; i++) {
102+
const part = cookieParts[i].toLowerCase();
103+
104+
// Check for existing attributes
105+
if (part.startsWith('domain=')) {
106+
// Replace domain with the domain from the request URL
107+
newCookieParts.push(`Domain=${url.hostname}`);
108+
hasDomain = true;
109+
} else if (part.startsWith('samesite=')) {
110+
// Keep original SameSite or set to None for cross-domain cookies
111+
newCookieParts.push('SameSite=None');
112+
hasSameSite = true;
113+
} else if (part === 'secure') {
114+
newCookieParts.push('Secure');
115+
hasSecure = true;
116+
} else {
117+
// Keep all other attributes as they are
118+
newCookieParts.push(cookieParts[i]);
119+
}
120+
}
121+
122+
// Add missing attributes if needed
123+
if (!hasDomain) {
124+
newCookieParts.push(`Domain=${url.hostname}`);
125+
}
126+
127+
if (!hasSameSite) {
128+
newCookieParts.push('SameSite=None');
129+
}
130+
131+
if (!hasSecure) {
132+
newCookieParts.push('Secure');
133+
}
134+
135+
return newCookieParts.join('; ');
123136
}
137+
138+
// Export default for compatibility with tests
139+
export default {
140+
fetch: handleRequest
141+
} satisfies ExportedHandler<Env>;

0 commit comments

Comments
 (0)