How To Host Static Website On Google App Engine

July 26, 2017

Why I choose App Engine for Static Web Hosting

I wanted a setup without DevOps, so hosting on EC2 or DigitalOcean is out (not to mentioned the need to integrate CDN after that).

I wanted to try Google Cloud Storage, but SSL support comes with additional service and cost.

S3 + Cloudfare seems popular, but the task of setting up both things seems daunting.

App Engine seems fairly simple and free for low traffic site (static files shouldn’t count towards instance cost, only need to pay for bandwidth). Static files seem to be distributed on multiple nodes with good performance (whether they are edge-cache nodes is debatable). If I ever need to write some server-side code, it could be easily done.

App Engine Limitation

Prerequisite

Create a project on Google Cloud Platform.

  • Remember your Project ID

Create an App Engine app

  • Language: Python (pick a language you are familar with, though it doesn’t matter for static website)
  • Region: us-central (depending on your audience)
  • Don’t have to proceed with the Quickstart Tutorial.

Create App Engine

Install Google Cloud SDK

Download the latest Google Cloud SDK

Extract the package (e.g. tar -zxf google-cloud-sdk*.tar.gz)

Run install script to add SDK rools to your path

./google-cloud-sdk/install.sh
# Output
Modify profile to update your $PATH and enable shell command
completion? [Y]
Enter a path to an rc file to update, or leave blank to use
[ENTER]

Initialize the SDK (enter your Google credential and select Project ID)

./google-cloud-sdk/bin/gcloud init

# You can respond "n" to the following
API [compute-component.googleapis.com] not enabled on project
[793702336627]. Would you like to enable and retry?  (Y/n)?

Create App Engine project files

Create a directory for your app engine project.

mkdir hello-world-app
cd hello-world-app

Create an app.yaml file.

runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /robots.txt
  static_files: www/robots.txt
  upload: www/robots.txt
  secure: always

# file with extensions (longer cache period)
- url: /(.*\.(css|js|woff|woff2|ico|png|jpg))
  static_files: www/\1
  upload: www/(.*)
  expiration: "14d"
  secure: always

# file with extensions
- url: /(.*\..*)
  static_files: www/\1
  upload: www/(.*)
  secure: always

# assume file without extensions use index.html
- url: /(.*)/
  static_files: www/\1/index.html
  upload: www/(.*)/index.html
  secure: always

- url: /
  static_files: www/index.html
  upload: www/index.html
  secure: always

NOTE: To use secure: always, remember to enable managed SSL certificates for your website.

Create www directory

mkdir www
cd www

Create index.html in www

<html>
  <head>
    <title>Hello World</title>
    <link rel="stylesheet" type="text/css" href="/css/style.css">
  </head>
  <body>
    <h1>Hello World</h1>
    <p class="red">I am Red</a>
    </p>
  </body>
</html>

Create css directory in www

mkdir css
cd css

Create style.css in www/css

.red {
  color: #FF0000;
}

Project files directory structure

hello-world-app
├── app.yaml
└── www
    ├── css
    │   └── style.css
    └── index.html

Deploy the app and make sure the source and Project ID is correct

Deployment

Deploy local files to App Engine server.

gcloud app deploy -v 1
# output
Services to deploy:

descriptor:      [/hello-world-app/app.yaml]
source:          [/hello-world-app]
target project:  [hello-world-project-id]
target service:  [default]
target version:  [1]
target url:      [https://hello-world-project-id.appspot.com]


Do you want to continue (Y/n)?  Y

Launch brower to preview the website

gcloud app browse -v 1

I prefer to include version (-v 1), else a new version will be created for every upload.

Static cache expiration

When you make changes to the files and refresh the page the second time, you might notice the page still serve the old content. All static contents are cached (default 10-minute expiration time) once downloaded, with no way to clear the cache (clearing browser cache and re-deploying a new version won’t clear the cache) until it expires.

You can change cache duration by changing default_expiration or expiration in app.yaml.

For development purpose, you can add a caching busting query string to the end of the url (e.g. https://hello-world-project-id.appspot.com?r=1)

Refer to static cache expiration.

Web Server Performance

Google App Engine server performance is exceptionally good accross multiple countries.

Bitcatcha Result

Location Response Times
US (W) 1 ms
US (E) 3 ms
London 25 ms
Singapore 12 ms
Sao Paulo 52 ms
Bangalore 93 ms
Sydney 335 ms
Japan 70 ms

Bitchacha Result

Pagespeed Insights Result

Your server responded quickly.

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.