Building a secure static site

Stay organized with collections Save and categorize content based on your preferences.

For purely static sites (not a single line of dynamic backend code), the security team have approved the use of a simple app.yaml file in which static files can be served safely and securely with all appropriate security headers in place. This tutorial will show you how to use the approved app.yaml to create purely static HTML websites.

You will learn how to:

  • Create an app.yaml file which can serve static files
  • Create the static site
  • Deploy your site to App Engine
  • Useful links

Prerequisites:

Create the app.yaml configuration file

The following app.yaml shows how to serve static files directly from App Engine with all appropriate security headers in place. Do not remove any CSP directives without first consulting your ATL. If you add any URLs to the policy ensure it validates correctly using the CSP evaluator at

https://csp-evaluator.withgoogle.com/. More information about serving safe CSP headers can be found here.

runtime: python39
handlers:

# PDF files
- url: /(.*\.pdf)
  secure: always
  static_files: dist/\1
  upload: dist/(.*)
  http_headers:
    X-Frame-Options: "DENY"
    Strict-Transport-Security: "max-age=2592000; includeSubdomains"
    X-Content-Type-Options: "nosniff"
    X-XSS-Protection: "1; mode=block"
    Content-Security-Policy:
      base-uri 'none';
      script-src 'self'
        *.google.com
        *.google-analytics.com
        *.googletagmanager.com
        *.gstatic.com
        *.youtube.com
        *.ytimg.com;

# Static assets (anything with a file extension)
- url: /(.*\..*)
  secure: always
  static_files: dist/\1
  upload: dist/(.*)
  http_headers:
    X-Frame-Options: "DENY"
    Strict-Transport-Security: "max-age=2592000; includeSubdomains"
    X-Content-Type-Options: "nosniff"
    X-XSS-Protection: "1; mode=block"
    Content-Security-Policy:
      base-uri 'none';
      object-src 'none';
      script-src 'self'
        *.google.com
        *.google-analytics.com
        *.googletagmanager.com
        *.gstatic.com
        *.youtube.com
        *.ytimg.com;

# Root
- url: /
  secure: always
  static_files: dist/index.html
  upload: dist/(.*)
  http_headers:
    X-Frame-Options: "DENY"
    Strict-Transport-Security: "max-age=2592000; includeSubdomains"
    X-Content-Type-Options: "nosniff"
    X-XSS-Protection: "1; mode=block"
    Content-Security-Policy:
      base-uri 'none';
      object-src 'none';
      script-src 'self'
        *.google.com
        *.google-analytics.com
        *.googletagmanager.com
        *.gstatic.com
        *.youtube.com
        *.ytimg.com;

# Paths with a trailing slash
- url: /(.*\/)
  secure: always
  static_files: dist/\1index.html
  upload: dist/(.*)
  http_headers:
    X-Frame-Options: "DENY"
    Strict-Transport-Security: "max-age=2592000; includeSubdomains"
    X-Content-Type-Options: "nosniff"
    X-XSS-Protection: "1; mode=block"
    Content-Security-Policy:
      base-uri 'none';
      object-src 'none';
      script-src 'self'
        *.google.com
        *.google-analytics.com
        *.googletagmanager.com
        *.gstatic.com
        *.youtube.com
        *.ytimg.com;

# Paths without a trailing slash
- url: /(.*)?
  secure: always
  static_files: dist/\1/index.html
  upload: dist/(.*)
  http_headers:
    X-Frame-Options: "DENY"
    Strict-Transport-Security: "max-age=2592000; includeSubdomains"
    X-Content-Type-Options: "nosniff"
    X-XSS-Protection: "1; mode=block"
    Content-Security-Policy:
      base-uri 'none';
      object-src 'none';
      script-src 'self'
        *.google.com
        *.google-analytics.com
        *.googletagmanager.com
        *.gstatic.com
        *.youtube.com
        *.ytimg.com;

Create the static site

We have set up the App Engine configuration in such a way that folders define the URL structure, this allows us to use clean or "pretty" URLs without each page address ending with a .html extension.

Our entire static site will live in the /dist folder at the root of our project. Each URL will map to the associated file path. Let’s take the following example and implement it.

/                 =>  dist/index.html
/offices          =>  dist/offices/index.html
/offices/london   =>  dist/offices/london/index.html

Firstly create the folder structure (replace "my_project" with your project path).

mkdir -p ~/my_project/dist/offices/london

Now create the files and with your text editor copy and paste the content into the associated file.

touch ~/my_project/dist/index.html
touch ~/my_project/dist/offices/index.html
touch ~/my_project/dist/offices/london/index.html

dist/index.html

<html>
  <head>
    <link href="/static/css/styles.css" rel="stylesheet">
  </head>
  <body>
    <h1>Hello world!</h1>
    <a href="/offices">View our offices</a>
  </body>
</html>

dist/offices/index.html

<html>
  <head>
    <link href="/static/css/styles.css" rel="stylesheet">
  </head>
  <body>
    <h1>Google offices</h1>
    <a href="/offices/london">View the London office</a>
  </body>
</html>

dist/offices/london/index.html

<html>
  <head>
    <link href="/static/css/styles.css" rel="stylesheet">
  </head>
  <body>
    <h1>London office</h1>
    <img src="https://goo.gl/Xnvdb9" alt="office">
  </body>
</html>

Let’s create our CSS file add some styling.

mkdir -p ~/my_project/dist/static/css
touch ~/my_project/dist/static/css/styles.css

dist/static/css/styles.css

body {
  background-color: #eee;
  font-family: sans-serif;
  padding: 20px;
}

Now we have all of our files in place let’s run the server and ensure our static site is working.

dev_appserver.py app.yaml

Visit http://localhost:8080/ and test your static site.

Deploy your site to App Engine

We first need to create our App Engine project. Visit the Google Cloud Console and click Create Project and take note of the application ID. In the example below the application ID is "polished-talon-125616".

Once we have created our project we can deploy to it. If this is the first time deploying an application to App Engine you may be asked log into your Google account via your web browser when running the following commands.

gcloud app deploy app.yaml --project $[your_application_ID]

App Engine SDK https://cloud.google.com/appengine/downloads

Google cloud console https://console.developers.google.com

CSP documentation https://csp.withgoogle.com/docs/index.html

CSP evaluator https://csp-evaluator.withgoogle.com/

Google partner security documentation http://g.co/partner-security

Using Cookiecutter with the Secure Scaffold to create static websites https://goto.google.com/gae-scaffold3