Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save DementedEarplug/da0ef97effcb3e416f8efafb1b2f8b33 to your computer and use it in GitHub Desktop.
Save DementedEarplug/da0ef97effcb3e416f8efafb1b2f8b33 to your computer and use it in GitHub Desktop.
Proxy everything
// Adjust proxy server to rewrite URLs
// Main proxy route that handles all incoming requests
// Takes a URL parameter that specifies which resource to proxy
app.get("/proxy/:url", async (req, res) => {
// Decode the URL parameter since it comes URL-encoded
const url = decodeURIComponent(req.params.url);
// Create URL object to help with resolving relative URLs later
const baseUrl = new URL(url);
try {
// Fetch the target URL content as a raw buffer to handle different content types
const response = await axios.get(url, { responseType: "arraybuffer" });
const contentType = response.headers["content-type"];
// Forward the original content type header to maintain proper content handling
if (contentType) {
res.setHeader("Content-Type", contentType);
}
let html = response.data;
// Special handling for HTML content to modify internal resources
if (contentType && contentType.includes("text/html")) {
// Convert buffer to string for HTML processing
html = Buffer.from(html, "binary").toString();
// Parse HTML with Cheerio (jQuery-like API for server-side)
const $ = cheerio.load(html);
// Process all stylesheet links to proxy them through our server
// This ensures CSS files are also fetched through our proxy
$('link[rel="stylesheet"]').each((_, element) => {
const href = $(element).attr("href");
if (href) {
// Convert relative URLs to absolute using the base URL
const absoluteUrl = new URL(href, baseUrl.origin);
// Rewrite href to go through our proxy
$(element).attr(
"href",
`${req.protocol}://${req.get("host")}/proxy/${encodeURIComponent(
absoluteUrl.href
)}`
);
}
});
// Process all images to proxy them through our server
// Similar to stylesheet processing
$("img").each((_, element) => {
const src = $(element).attr("src");
if (src) {
const absoluteUrl = new URL(src, baseUrl.origin);
$(element).attr(
"src",
`${req.protocol}://${req.get("host")}/proxy/${encodeURIComponent(
absoluteUrl.href
)}`
);
}
});
// Remove autoplay attributes from video elements
// This prevents unwanted automatic video playback
$("video[autoplay]").removeAttr("autoplay");
$('video[playsinline]').removeAttr('playsinline');
// Handle iframe elements, particularly for video embeds
// Modify their URLs to disable autoplay functionality
$("iframe").each((_, element) => {
const src = $(element).attr("src");
if (src) {
// Special handling for YouTube and Vimeo iframes
if (src.includes("youtube.com") || src.includes("vimeo.com")) {
const separator = src.includes("?") ? "&" : "?";
const newSrc = `${src}${separator}autoplay=0`;
$(element).attr("src", newSrc);
}
}
});
// Handle base tags and links
// Remove base tags as they can interfere with our URL rewriting
$("base").remove();
// Process all anchor tags to proxy their destinations
$("a").each((_, element) => {
const href = $(element).attr("href");
if (href) {
const absoluteUrl = new URL(href, baseUrl.origin);
$(element)
.attr(
"href",
`${req.protocol}://${req.get("host")}/proxy/${encodeURIComponent(
absoluteUrl.href
)}`
)
// Force links to open in the same window to maintain proxy context
.attr("target", "_self");
}
});
// Inject JavaScript to handle client-side behavior
// This script ensures proper link handling and video control
$("body").append(`<script>
// Intercept clicks on proxied links to handle them properly
document.addEventListener('click', (evt) => {
const target = evt.target;
if (target && target.getAttribute('href')?.startsWith('/proxy/')) {
evt.preventDefault();
window.location.href = target.getAttribute('href');
}
});
// Ensure all videos are paused when the page loads
document.addEventListener('DOMContentLoaded', function() {
const videos = document.querySelectorAll('video');
videos.forEach(video => {
video.pause(); // Ensure videos are paused
});
});
</script>`);
// Send the modified HTML back to the client
res.send($.html());
} else {
// For non-HTML content (CSS, images, etc.), send the raw buffer
res.send(response.data);
}
} catch (error) {
// Log any errors and send an error response
console.error("Error fetching content", error);
res.status(500).send("Error fetching content");
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment