javascriptpythondjangodnsfriendly-url

How to hide webapi url and show a "pretty" url?


I need to generate a pretty URL for my own URL shortening service.
My web server address looks something like https://my-backend-api-server.us-central1.run.app/redirectapp/redirect/wAzclnp3

and I wouldn't want to expose that, nor is it short.

Assuming I have a domain www.mydomain123.com, I want to "prettify" my URLs so that www.mydomain123.com/wAzclnp3 will serve https://my-backend-api-server.us-central1.run.app/redirectapp/redirect/wAzclnp3 and www.mydomain123.com or www.mydomain123.com/otherapp will serve from my webserver (not the api server)

How to do this in django ?


Solution

  • I ended up using a reverse proxy as suggested by @grawity_u1686

    I set up a worker in CloudFlare.com (and locally with wrangler) The code (which was more complex than I had hoped) is following. I had to also support assets, and change my backend to only return the hash itself and not a redirect object.

    addEventListener('fetch', event => {
        event.respondWith(handleRequest(event.request));
    });
    
    async function handleRequest(request) {
        const url = new URL(request.url);
        const path = url.pathname;
    
        console.log(`Incoming request URL: ${url}, Path: ${path}`);
    
        // Handle /r/{somehash} redirects
        if (path.startsWith('/r/')) {
            const hash = path.substring(3); // Extract "somehash" from "/r/{somehash}"
            
            const backendUrl = `https://backend-1234.us-central1.run.app/app/unhash/${hash}`;
            
            try {
                console.log(`Fetching URL from backend: ${backendUrl}`);
    
                // Fetch the URL from the backend
                const response = await fetch(backendUrl, {
                    method: 'GET',
                    headers: request.headers,
                });
    
                if (!response.ok) {
                    throw new Error(`Backend responded with status ${response.status}`);
                }
    
                console.log(response);
    
                const data = await response.json();
                const resolvedUrl = data.long_url;
    
                if (!resolvedUrl) {
                    throw new Error('No URL found in backend response');
                }
    
                console.log(`Resolved URL from backend: ${resolvedUrl}`);
    
                return Response.redirect(resolvedUrl, 302);
            } catch (error) {
                console.error(`Error resolving /r/${hash}: ${error.message}`);
                return new Response(`Error resolving the redirect: ${error.message}`, {
                    status: 502,
                    headers: { 'Content-Type': 'text/plain' },
                });
            }
        }
    
        // Handle static file requests
        if (path.startsWith('/static/') || path.startsWith('/assets/')) {
            const staticUrl = `https://myaccount.github.io/personal_website${path}`;
            try {
                const staticResponse = await fetch(staticUrl, {
                    method: 'GET',
                    headers: request.headers,
                    redirect: 'follow',
                });
    
                if (!staticResponse.ok && staticResponse.status !== 404) {
                    throw new Error(`Static file fetch failed with status ${staticResponse.status}`);
                }
    
                return staticResponse;
            } catch (error) {
                console.error(`Error fetching static file: ${error.message}`);
                return new Response(`Error fetching static file: ${error.message}`, {
                    status: 502,
                    headers: { 'Content-Type': 'text/plain' },
                });
            }
        }
    
        // Default behavior: Proxy other requests to GitHub Pages
        const githubUrl = `https://myaccount.github.io/personal_website${path}`;
        try {
            let response = await fetch(githubUrl, {
                method: request.method,
                headers: request.headers,
                body: request.method !== 'GET' && request.method !== 'HEAD' ? request.body : null,
                redirect: 'follow',
            });
    
            if (response.status === 404) {
                // Serve index.html for client-side routing
                const indexUrl = `https://myaccount.github.io/personal_website/index.html`;
                response = await fetch(indexUrl, {
                    method: 'GET',
                    redirect: 'follow',
                });
            }
    
            // Clone and modify headers
            let newHeaders = new Headers(response.headers);
            newHeaders.set('Access-Control-Allow-Origin', '*');
    
            return new Response(response.body, {
                status: response.status,
                statusText: response.statusText,
                headers: newHeaders,
            });
        } catch (error) {
            console.error(`Error fetching GitHub Pages: ${error.message}`);
            return new Response(`Error fetching GitHub Pages: ${error.message}`, {
                status: 502,
                headers: { 'Content-Type': 'text/plain' },
            });
        }
    }