Navigating Redirect Challenges With GitHub Pages: A Creative Approach to Domain Migration¶
Introduction¶
Imagine having a GitHub Pages website. Now, you've migrated to project on another GitHub Pages website. As reports surfaced about users being unable to access the site, the need for a swift redirection from old URLs to the current ones became paramount.
The catch? The solution had to operate within the constraints of a static web page, using only HTML, CSS, and JavaScript.
While conventional methods like Flask and Frozen-Flask failed, the journey led to a creative solution using HTML and JavaScript. In this blog post, I'll share the step-by-step process of how I navigated through the obstacles and achieved seamless redirection.
The Initial Attempts: Static HTML¶
Using HTML for Single Page Redirect¶
My first inclination was to use HTML for redirection. To redirect only one page, a simple HTML script could be used. For instance:
Using HTML for Single Page Redirect
Static HTML Challenges¶
However, it quickly became apparent that defining redirects for every page statically was impractical, as I would need to create an HTML page for each endpoint. Then, I thought of using React or Flask, but they require explicitly defined routes too.
Regardless, there is a module that uses Flask to implement the first option. So, I can combine the list of URLs with the Frozen-Flask module.
The existence of this URL list proved crucial, as without it, I would have encountered additional difficulties.
Another Way: Flask and Frozen-Flask¶
Next, I explored Flask and Frozen-Flask, but challenges arose when dealing with dynamic endpoints in a static context. The attempt to freeze the Flask app yielded an error, highlighting the limitations of this approach.
First Attempts with Flask¶
Prerequisites
Before diving into the implementation, I set up the development environment with the following commands:
After environment setup, I've created three python files
utils.pyto parse the sitemap.xml file and extract the necessary endpointsapp.pyresponsible for handling the redirection logicmain.pyconvert the Flask app into a static website usingfrozen-flaskmodule
First Attemps with Flask
import xml.etree.ElementTree as ET
# Load the XML file
xml_file_path = "sitemap.xml"
tree = ET.parse(xml_file_path)
root = tree.getroot()
# Define the namespace
namespace = {'ns': 'http://www.sitemaps.org/schemas/sitemap/0.9'}
WEBSITE_URL = "https://hermann-web.github.io/blog"
def endpoint_parser(endpoint:str):
if endpoint.startswith("/"):
endpoint = endpoint[1:]
if endpoint.endswith("/"):
endpoint = endpoint[:-1]
return endpoint
# Extract endpoint URLs
def get_endpoints():
endpoints = [url_element.text for url_element in root.findall('.//ns:loc', namespace)]
endpoints = [endpoint_parser(url.replace(WEBSITE_URL, "")) for url in endpoints]
return endpoints
from flask import Flask, redirect
from utils import WEBSITE_URL, endpoint_parser
app = Flask(__name__)
target_domain = WEBSITE_URL
assert target_domain
@app.route('/')
def index():
return redirect(target_domain)
# Create a route for redirection
@app.route('/<path:endpoint>')
def redirect_to_another_server(endpoint):
endpoint = endpoint_parser(endpoint)
print("endpoint:",endpoint)
if endpoint in endpoints:
target_url = f"{target_domain}/{endpoint}"
return redirect(target_url)
else:
return redirect(target_domain)
if __name__ == "__main__":
app.run(debug=True)
Running the app went fine:
* Serving Flask app 'app'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
However, running python main.py to freeze the Flask app using Frozen-Flask failed due to the inability to follow external redirects.
This makes sense. A static webpage cannot accept dynamic endpoints. So, I should create the endpoints manually using a for-loop, hoping it will work with Flask.
So, i've tried some workarounds
Workarounds with Flask¶
Workaround 1: Manually Creating Routes¶
However, failed. The issue was that I should not have duplicated function names for the routes:
Manually Creating Routes
from flask import Flask, redirect
from get_endpoints import get_endpoints, WEBSITE_URL, endpoint_parser
app = Flask(__name__)
target_domain = WEBSITE_URL
assert target_domain
endpoints = get_endpoints()
# Create route functions for each endpoint
for endpoint in endpoints:
@app.route(f'/{endpoint}')
def redirect_to_another_server():
target_url = f"{target_domain}/{endpoint}"
return redirect(target_url)
if __name__ == "__main__":
app.run(debug=True)
A first attempt to automatically generate route functions in Flask failed, as redirect_to_another_server was duplicated:
AssertionError: View function mapping is overwriting an existing endpoint function: redirect_to_another_server
So, that approach faced an issue with function duplication, prompting a need for a workaround.
Workaround 2: Dynamic Function Generation¶
There is another solution that involves encapsulating the route functions within another function, ensuring a unique context for each endpoint.
Another solution can be to change the function name with a decorator, but it is not possible. So, I figured I can define the functions inside another function, hoping it will work.
Dynamic Function Generation
The attempt to generate dynamic functions also faced an issue with function duplication.
AssertionError: View function mapping is overwriting an existing endpoint function: dynamic_function
Final Solution: Overcoming a Static Constraint¶
When attempting to freeze the Flask app using Frozen-Flask, a runtime error occurs due to the inability to follow external redirects. This limitation is inherent in static web pages, preventing the use of dynamic endpoints.
To work around this constraint, a custom 404.html page is created, embedding JavaScript to correct the URL before redirecting. This clever solution ensures that even erroneous URLs lead users to the correct destination.
<!-- 404.html -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="en">
<head>
<title>Page Not Found</title>
<script>
// Redirect logic to correct the URL
let new_url = "/";
if (window.location.href.startsWith("https://hermann-web.github.io/web")) {
new_url = window.location.href.replace("https://hermann-web.github.io/web", "/blog");
}
window.location.replace(new_url);
</script>
</head>
<body></body>
</html>
I noticed that all erroneous URLs redirect the user to the 404.html page. For GitHub Pages, I made the remark that even on the 404.html page, the erroneous URLs are conserved in the browser. So, I can just correct the last endpoint /web/* to the correct one /blog/* using JavaScript.
Conclusion¶
In this guide, we explored a step-by-step approach to redirecting all pages from one domain to another using Flask and Frozen-Flask. From parsing the sitemap.xml file to handling dynamic endpoints and overcoming static constraints, each aspect was covered in detail. The use of a custom 404.html page with JavaScript ensures a smooth redirection experience for users, making this solution both effective and elegant.