Use Google App Engine and Python for Inexpensive Domain Redirects

Monday, March 6, 2017

This blog has existed under a few domain names: workingconfig.com, thornelaboratories.net, and, now, thornelabs.net. workingconfig.com didn’t exist for very long, and never had many - if any - backlinks to it; it didn’t make sense to keep those URLs alive. thornelaboratories.net, on the other hand, did exist for a longer period of time, and it had enough URLs I wanted to keep alive.

I can easily host thornelaboratories.net and thornelabs.net from the same webserver on a single VM - I never needed more resources to serve static content.

The configuration redirects for thornelaboratories.net lived in its own VirtualHost when I ran Apache, and then its own Server Block when I migrated to nginx. This worked well, but I was managing the nginx configurations for thornelaboratories.net and thornelabs.net in the same git repository, and I wanted to seperate the two domains even more; I wanted to host thornelaboratories.net somewhere else and not have to think about it any more, but I didn’t want to create another VM for it because it would be a waste of resources to redirect so little traffic. I wanted to host it somewhere new and different, and spend as little money as possible.

Then I found Max Laumeister’s blog post on how to use Google App Engine as a free redirect server. This was exactly what I was looking for, and, given how little traffic thornelaboratories.net redirects, hosting the redirect service on Google App Engine would easily fit within the limits of App Engine’s free tier.

Prerequisites

This post assumes the following:

  • You are familiar with Google Cloud Platform (GCP) and have already created a GCP Project
  • You have installed the Google Cloud SDK
  • You have authenticated the gcloud command against your Google Account

Create a GCP Project

If you have not yet created a GCP Project, follow these steps:

  1. Open a web browser, and create or log in to a Google Account
  2. Navigate to the GCP Console
  3. If this is your first GCP Project, you will be prompted to create a GCP Project. Each Google Account gets $300 in credit to use within 12 months towards GCP. You are required to enter a credit card to create a GCP Project, but it will not be charged until the $300 credit is consumed or 12 months expire.
  4. If this is a new GCP Project, you will need to enable the Compute Engine API by navigating to the Compute Engine section of the GCP Console and wait for initialization to complete

Install the Google Cloud SDK

If you have not yet installed the Google Cloud SDK, follow the instructions here.

Authenticate gcloud

Once you have created a GCP Project and installed the Google Cloud SDK, the last step is to authenticate the gcloud command to your Google Account. Open your terminal application and run the following command:

gcloud auth login

A web page will open in your web browser. Select your Google Account and give it permission to access GCP. Once completed, you will be authenticated and ready to move forward.

Create a Directory

Next, create a directory somewhere on your workstation to store your Google App Engine application:

mkdir ~/Development/domain-redirect/app-engine

Change into that directory:

cd ~/Development/domain-redirect/app-engine

The remainder of this post will assume you are working inside of this directory.

Create app.yaml

Google App Engine typically requires two files: app.yaml and an application file written in Python, Golang, Java, or PHP - in this case it’s going to be Python.

app.yaml provides the necessary configuration to run your application. There are a lot of different parameters that can exist in app.yaml. Those parameters might differ based on the programming language used. For this post, Python will be used, and you can find all the available Python parameters here.

Create file app.yaml with the following contents:

runtime: python27
api_version: 1
threadsafe: yes

handlers:
- url: /.*
  script: main.app

Create main.py

Next, you need the Python application file.

Most of the following code comes from Max’s post, but I have made some modifications. I wanted to be very exact in what URLs I was redirecting; I did not want a catch-all redirect. So, I removed the DEFAULT_URL variable and redirection code originally found under the else blocks in method get under the MainPage class. If you prefer to have a catch-all redirect, refer to the aforementioned code block in Max’s post.

For the following code to meet your needs, create file main.py, and, in the urls dictionary, replace all of the key value pairs to match the redirects you need in place. Replace each key with just the path portion of the old URL you want to keep alive. Then replace each value with the exact new URL - domain and path - you want to redirect to. You more than likely want to keep the 301 redirect, but it can be changed if needed.

import webapp2
import logging
from urlparse import urlparse

# Path => URL to redirect to, redirect type (301 is permanent, 302 is temporary)
urls = {
    '/': ('https://new-domain-example.com', '301'),
    '/index.html': ('https://new-domain-example.com/index.html', '301'),
    '/post/example-post-1.html': ('https://new-domain-example.com/post/example-post-1.html', '301'),
    '/post/example-post-2.html': ('https://new-domain-example.com/post/example-post-2.html', '301'),
}

def get_redirect_url(url):
    scheme, netloc, path, params, query, fragment = urlparse(url)

    # Fix empty paths to be just '/' for consistency
    if path == '':
        path = '/'

    # Check if we have a mapping for this url
    if path in urls:
        return urls[path]
    else:
        return None, None

class MainPage(webapp2.RequestHandler):
    def get(self):
        url, perm = get_redirect_url(self.request.url)

        if url:
            self.redirect(url, permanent=True)

        else:
            logging.error('Unable to redirect this url: ' + self.request.url);

            self.error(404)
            self.response.write('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
            self.response.write('<title>404 Not Found</title>')
            self.response.write('<h1>Not Found</h1>')
            self.response.write('<p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>')

app = webapp2.WSGIApplication([
    ('/.*', MainPage),
])

Deploy to App Engine

With app.yaml and main.py saved, you are ready to deploy your Google App Engine application.

Assuming gcloud is already pointed at the Google Cloud Project you want to deploy to, verify with gcloud config list project, run the following command:

gcloud app deploy

The command will output the appspot URL your application will be deployed to and ask if you want to continue. Typically, the appspot URL is https://your-project-id.appspot.com. This is also a useful self-check to make sure you are not deploying your application to the wrong Google Cloud Project. If everything looks okay, type Y and Enter to deploy your application. After a few seconds, the application should be deployed.

Setup DNS

At this point, your application is deployed under URL https://your-project-id.appspot.com. Unless your website used that as its old domain name, you probably want to setup a custom domain that uses your actual old domain name.

The App Engine section of the Google Cloud Console can be used to do this. Go here and follow the instructions to configure your custom domain.

Once that is complete and DNS has had time to propagate, you should be able to navigate to http://old-domain-example.com/post/example-post-1.html in your web browser and have it redirect to http://new-domain-example.com/post/example-post-1.html.

References

Generic 301 Redirection Script for Google App Engine

comments powered by Disqus