Decipher Tools Blog

News about all things iOS, especially backup, recovery, and forensics. Upcoming features for Decipher TextMessage, Decipher Backup Repair, Decipher VoiceMail, and Decipher Backup Browser.


How To Run a (Mostly) Static Website in Google App Engine

Kelly Wilkerson | | categories: development | View Comments

At the beginning of 2015, I moved all of the deciphertools.com website to the Google App Engine. Most of the content on our website is static and our traffic is moderate but bursty, so running our own virtual server on Rackspace to host the website seemed wasteful (of administration time and money.) Our virtual server was also performing poorly: during those bursty times, we would have poor latency, and our website was very open to (extremely lame) DoS attacks.

After the move, our website is cheaper to run, and performs beautifully. I can rest easy at night knowing that if we have spikes in traffic (which should be cause for celebration), our infrastructure will scale to handle the load. To this day our static site costs have been free. We do pay for some other dynamic site projects as well as Google Cloud Storage to host our larger software downloads, at rates comparable to Rackspace Cloud Files. The entirety is still MUCH cheaper than spinning up a Rackspace cloud server ourselves and running lighttpd or apache.

Finding simple directions to host static pages on the Google App Engine was difficult, so this is my contribution of instructions. Please feel free to post comments with questions — questions usually make me learn something new!

Step 0: Download the App Engine SDK

Download the SDK (and administration program) from the App Engine download page. If you're hosting a truly static site, then it doesn't matter which programming language you pick. However, most sites end up needing some redirections and email scripts, so pick something you're comfortable scripting in. If you're used to web scripting in PHP and the site is really only going to do basic stuff like redirection and email, then PHP is great. If you're going to want to use the App Engine datastore and other APIs eventually, then I recommend Python.

Step 1: Setup your project folder structure

Every app engine project has an app.yaml file, and then all of the supporting scripts and files. For (mostly) static sites, I like to have the following folder structure:

- my_project
  |- app_engine
     |- app.yaml
     |- public
        |- (all of my static site files, examples here below...)
        |- index.html
        |- favicon.ico
        |- images
           |- image1.png
           |- image2.png
        |- js
           |- bootstrap.min.js
        |- css
           |- ... you get the idea

I make that app_engine folder to house the Google App Engine project in case I have external resources to generate pieces of the website. For example, I use blogofile to generate our website and blog, so in addition to the app_engine folder, I also have a blogofile project that houses the blogofile templates. (I generate the site files from my blogofile templates, then copy the results into app_engine/public when I am happy with them.)

Step 1: Setup your static file folder

The public folder is going to house the static files that your site serves. Structure this just like it were your normal site folder on a web-server setup. In my cool ascii-art diagram above, you can see a typical index page, images subfolder, and Javascript/CSS folders.

Step 2: Make a simple app.yaml for all static content

You might be appalled by the cases, I'm ok with that!

application: your-application-name-here
version: 1
runtime: php
api_version: 1
threadsafe: yes

handlers:

# Handle the main page by serving the index page.
# Note the $ to specify the end of the path, since app.yaml does prefix matching.
- url: /$
  static_files: public/index.html
  upload: public/index.html


# Handle folder urls by serving the index.html page inside.
- url: /(.*)/$
  static_files: public/\1/index.html
  upload: public/.*/index.html

# Handle nearly every other file by just serving it.
- url: /(.+)
  static_files: public/\1
  upload: public/(.*)


# Recommended file skipping declaration from the GAE tutorials
skip_files:
  - ^(.*/)?app\.yaml
  - ^(.*/)?app\.yml
  - ^(.*/)?#.*#
  - ^(.*/)?.*~
  - ^(.*/)?.*\.py[co]
  - ^(.*/)?.*/RCS/.*
  - ^(.*/)?\..*
  - ^(.*/)?tests$
  - ^(.*/)?test$
  - ^test/(.*/)?
  - ^COPYING.LESSER
  - ^README\..*
  - \.gitignore
  - ^\.git/.*
  - \.*\.lint$
  - ^fabfile\.py
  - ^testrunner\.py
  - ^grunt\.js
  - ^node_modules/(.*/)?

Step 3: Handling your 404 baggage with redirects

Our website has evolved over many years, so we have a hefty pile of 301 redirects that need serving. To serve the 301 redirect header, you need to use some scripting; for this example I'm using PHP. (If anyone knows how to serve the 301 statically, I would LOVE to know how.) You may be grossed out by the following script, but it works, and it reminds me of lighttpd's mod_redirect, so I'm happy enough.

  • I setup a script named redirector.php. I was lazy and it's just sitting in my app_engine folder; it would probably be better in a scripts folder or something clean like that.
<?
// REMINDER: ALL OF THESE NEED TO BE IN app.yaml too
$direct_redirects = array(
  "/blog" => "https://yoururl.com/blog/",
  "/products.html" => "https://yoururl.com/index.html",
  ... many many MANY... MANY other mappings...
);

$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$redirect_url = $direct_redirects[$path];
if(!is_null($redirect_url)) {
  header("HTTP/1.1 301 Moved Permanently"); 
  header("Location: $redirect_url"); 
}
?>

(Yep, you have to map every url you want to redirect. Get fancier if you need, but I just use this to map crawl errors and moved pages, so it works for me.)

  • As the script so nicely reminds me, the urls that need redirection need to be in your app.yaml file. Add your paths to the handlers section ABOVE the other rules, since those rules match many things.
# Note the $ to specify the end of the path, since app.yaml does prefix matching.
- url: /blog$|/products.html$
  script: redirector.php

Step 4: Installing SSL and Setting Up Your Domain with Google Apps

If you want to serve your site using HTTPS, then you'll need to install SSL certificates in GAE.

(Note: I originally wrote this in a comment for this article below, but it's important enough to add in the main body!)

(Depending on your SSL needs, you can also look down in Step 5 about using Cloudflare to provide free SSL.)

If you need to install SSL on your App Engine app, you will need to setup your domain with Google Apps. If you want to support us, you can use our Google Apps referral link to sign up for Google Apps. Update 2016: You no longer need a Google Apps account to install SSL certificates on your App Engine site. Refer to the “Adding SSL to your custom domain” section of the instructions from Google in the next paragraph.

I don't know if there is something wrong with me, but I never remember how to do HTTPS/SSL setup. There are copious outdated documentation pages lurking around, along with poor instructions from third parties. I HIGHLY recommend these instructions from Google augmented with these instructions about the actual SSL installation from the Neutron Drive Blog.

A little bit about CloudFlare

If you need CDN caching, threat mitigation, SSL, site redirections, and a pile of other awesome services, I highly recommend CloudFlare. We used to use the Pro Plan for the Decipher Tools website, and I use the free plan for every static site I setup (at a bare minimum to use their lovely DNS interface that doesn't make me want to shove a spoon in my eye like most others.) Now that we have a static site, and our own SSL setup from step 4, we don't need the paid CloudFlare features. However I still really dig their DNS interface, and their caching is excellent if you have need.