Skip to content

Instantly share code, notes, and snippets.

@hutt
Last active January 5, 2025 00:31
Show Gist options
  • Select an option

  • Save hutt/62e9355afb0d4ff0eeecd39bc51652de to your computer and use it in GitHub Desktop.

Select an option

Save hutt/62e9355afb0d4ff0eeecd39bc51652de to your computer and use it in GitHub Desktop.
This Cloudflare Worker proxies and caches requests for privacy-friendly light-yt.js YouTube video embeds.
// This Cloudflare Worker proxies and caches requests for privacy-friendly light-yt.js YouTube video embeds.
// https://gist.github.com/hutt/62e9355afb0d4ff0eeecd39bc51652de
// Base URLs for YouTube no-cookie and YouTube image CDN
const YT_NOCOOKIE_URL = 'https://www.youtube-nocookie.com';
const YTIMG_URL = 'https://i.ytimg.com';
// Path prefix for our proxy
const PROXY_PATH = '/yt-proxy';
// Default cache duration in seconds (1 day)
const DEFAULT_CACHE_DURATION = 1 * 24 * 60 * 60;
// Listen for incoming fetch events
addEventListener('fetch', event => {
event.respondWith(handleRequest(event));
});
async function handleRequest(event) {
try {
const request = event.request;
const url = new URL(request.url);
const path = url.pathname;
// Check if the request path starts with our proxy path
if (path.startsWith(PROXY_PATH)) {
// Extract the sub-path after the proxy path
const subPath = path.slice(PROXY_PATH.length + 1);
// Split the sub-path to get the proxy type and target path
const [proxyType, ...restPath] = subPath.split('/');
const targetPath = restPath.join('/');
let targetUrl;
let isJson = false;
// Determine the target URL based on the proxy type
if (proxyType === 'data') {
targetUrl = new URL(`${YT_NOCOOKIE_URL}/${targetPath}`);
isJson = true;
} else if (proxyType === 'thumbnail') {
targetUrl = new URL(`${YTIMG_URL}/${targetPath}`);
} else {
// Return an error for invalid proxy types
return new Response('Invalid proxy type', { status: 400 });
}
// Copy all query parameters from the original request to the target URL
for (const [key, value] of url.searchParams) {
targetUrl.searchParams.append(key, value);
}
// Try to get the response from cache first
const cache = caches.default;
let response = await cache.match(request);
if (!response) {
// If not in cache, create a new headers object without sensitive headers
const cleanHeaders = new Headers(request.headers);
cleanHeaders.delete('cf-connecting-ip');
cleanHeaders.delete('x-real-ip');
// Fetch the resource from the target URL
response = await fetch(targetUrl.toString(), {
headers: cleanHeaders,
method: 'GET',
});
if (response.ok) {
// Create a new response with custom headers
const newResponse = new Response(response.body, response);
// Set cache control headers
newResponse.headers.set('Cache-Control', `public, max-age=${DEFAULT_CACHE_DURATION}, stale-while-revalidate=${DEFAULT_CACHE_DURATION}`);
// Set ETag header
newResponse.headers.set('ETag', response.headers.get('ETag') || crypto.randomUUID());
if (isJson) {
// For JSON data, set the content type to application/json
newResponse.headers.set('Content-Type', 'application/json');
} else {
// For thumbnails, keep the original content type if it's an image
const contentType = response.headers.get('Content-Type');
if (contentType && contentType.startsWith('image/')) {
newResponse.headers.set('Content-Type', contentType);
}
// Remove Content-Encoding header for images as they're already compressed
newResponse.headers.delete('Content-Encoding');
}
// Put the response in cache
event.waitUntil(cache.put(request, newResponse.clone()));
return newResponse;
}
}
return response;
}
// If the path doesn't start with our proxy path, return a 404
return new Response('Not Found', { status: 404 });
} catch (error) {
// Return a 500 error if something goes wrong
return new Response(`Error: ${error.message}`, { status: 500 });
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment