Skip to content

Instantly share code, notes, and snippets.

@alexamies
Last active October 16, 2018 23:32
Show Gist options
  • Save alexamies/db324bfdf8a7cd25493bce47be0fdc5d to your computer and use it in GitHub Desktop.
Save alexamies/db324bfdf8a7cd25493bce47be0fdc5d to your computer and use it in GitHub Desktop.
Using Chrome Dev Tools with Google Cloud

Using Chrome Dev Tools with Google Cloud

This file contains instructions for optimizing a static web site on Google Cloud Platform with Chrome Developer Tools. The final solution is contained in index.html. Intermediate solutions have are given in files index1.html, etc.

Setting up a Static Website on Google Cloud Storage

Static content can be easily hosted on GCP using the GCS features for Hosting a Static Website. You will need a domain and point that to Cloud Storage by using a CNAME record.

Check that the right project is set

PROJECT=[Your project]
gcloud config set project $PROJECT

To create a bucket matching the domain you need to perform Domain-Named Bucket Verification. After doing that, create the bucket with the command

BUCKET=[Your domain]
gsutil mb gs://${BUCKET}

Copy the web site files

gsutil cp *.html gs://${BUCKET}
gsutil cp *.js gs://${BUCKET}

Enable the bucket as a web site

gsutil web set -m index.html -e notfound.html gs://${BUCKET}

Make the pages public

gsutil iam ch allUsers:objectViewer gs://${BUCKET}
gsutil acl ch -u AllUsers:R gs://${BUCKET}

Test the initial solution:

curl http://${BUCKET}/index1.html

Analyze the Website with Lighthouse

Navigate to the page http://${BUCKET}/index1.html in Chrome and ppen up Lighthouse in Chrome Developer Tools by clicking on the Audit tab. Run the analysis by clicking the 'Run Audits' button.

When I ran this in October 2019 the top item on the list was 'Does not respond with a 200 when offline.' You can enable a static website to be accessed offline by using a Service Worker to cache. The files main.js and sw.js implement that. However, service workers require HTTPS. This also the third item on the Lighthouse audit list: 'Does not use HTTPS.' So we will start by enabling HTTPS.

Enabling HTTPS for a Static Website wit an L7 Load Balancer

Here are the instructions to add HTTPS to a static web site in Google Cloud Storage by adding a HTTPS load balancer with the storage bucket as a backend.

Create a global static IP address:

IP_NAME=lb-ip
gcloud compute addresses create $IP_NAME \
    --ip-version=IPV4 \
    --global

Now add a Cloud Storage Bucket backend to the load balancer using the gcloud compute backend-buckets create command:

BACKEND_BUCKET_NAME=static-bucket
gcloud compute backend-buckets create $BACKEND_BUCKET_NAME \
    --gcs-bucket-name $BUCKET

Create a URL Map for content based load balancing using the gcloud compute url-maps create command:

URL_MAP_NAME=web-map
gcloud compute url-maps create $URL_MAP_NAME \
    --default-backend-bucket=$BACKEND_BUCKET_NAME

Add a path matcher using the gcloud compute url-maps add-path-matcher command:

PATH_MATCHER_NAME=bucket-matcher
gcloud compute url-maps add-path-matcher $URL_MAP_NAME \
    --default-backend-bucket=$BACKEND_BUCKET_NAME \
    --path-matcher-name $PATH_MATCHER_NAME \

Create a Google managed TLS certificate with the gcloud beta compute ssl-certificates create command:

CERT_NAME=${PROJECT}-cert
DOMAIN=[Your domain]
gcloud beta compute ssl-certificates create $CERT_NAME \
  --domains $DOMAIN

Create a target proxy with the gcloud compute target-https-proxies create command

PROXY_NAME=https-lb-proxy
gcloud compute target-https-proxies create $PROXY_NAME \
    --url-map $URL_MAP_NAME --ssl-certificates $CERT_NAME

Enable QUIC for the proxy

gcloud compute target-https-proxies update $PROXY_NAME \
    --quic-override=ENABLE

Create a forwarding rule with the gcloud compute forwarding-rules create command:

FWD_NAME=https-content-rule
gcloud compute forwarding-rules create $FWD_NAME \
    --address $IP_NAME \
    --global \
    --target-https-proxy $PROXY_NAME \
    --ports 443

Now that you are using a load balancer for the site, change the DNS record to an A record that refers to the static IP address used by the load balancer. You might need to wait for a while for this to take effect, depending on your DNS TTL.

Send traffic to the load balancer

curl https://$DOMAIN/index2.html

Run the analysis again in Chrome Dev Tools for the page index2.html, which has the service worker included. You should find that the No. 1 (no 200 offline) and No. 3 (no HTTPS) problems are now obsent from the list of Progressive Web App problems. The top problem is now 'User will not be prompted to Install the Web App.'

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>GCS Static Page</title>
</head>
<h1>GCS Static Page</h1>
<body>
<p>This is a GCS Static Page</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Understory_state_park.jpg/1920px-Understory_state_park.jpg"/></p>
<div>Source: <a href="https://en.wikipedia.org/wiki/Stokes_State_Forest"
>https://en.wikipedia.org/wiki/Stokes_State_Forest</a>
</div>
<p>
Previous pages:
<ul>
<ol><a href="index1.html">index1.html - initial version</a></ol>
<ol><a href="index2.html">index2.html - with service worker</a></ol>
</ul>
</p>
<script src=/main.js></script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>GCS Static Page</title>
</head>
<h1>GCS Static Page</h1>
<body>
<p>This is a GCS Static Page</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Understory_state_park.jpg/1920px-Understory_state_park.jpg"/></p>
<div>Source: <a href="https://en.wikipedia.org/wiki/Stokes_State_Forest"
>https://en.wikipedia.org/wiki/Stokes_State_Forest</a>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>GCS Static Page</title>
</head>
<h1>GCS Static Page</h1>
<body>
<p>This is a GCS Static Page</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Understory_state_park.jpg/1920px-Understory_state_park.jpg"/></p>
<div>Source: <a href="https://en.wikipedia.org/wiki/Stokes_State_Forest"
>https://en.wikipedia.org/wiki/Stokes_State_Forest</a>
</div>
<script src=/main2.js></script>
</body>
</html>
/*
* Copyright 2018 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*
*/
'use strict';
// Log performance timings
let pageNav = performance.getEntriesByType("navigation")[0];
let dnsTime = pageNav.domainLookupEnd - pageNav.domainLookupStart;
let connectTime = pageNav.connectEnd - pageNav.connectStart;
let tlsTime = 0;
if (pageNav.secureConnectionStart > 0) {
tlsTime = pageNav.connectEnd - pageNav.secureConnectionStart;
}
let ttfb = pageNav.responseStart - pageNav.requestStart;
let dt = pageNav.responseEnd - pageNav.responseStart;
let transferSize = pageNav.transferSize;
let nextHopProtocol = pageNav.nextHopProtocol;
console.log(`DNS lookup: ${ dnsTime }`);
console.log(`Connect Time: ${ connectTime }`);
console.log(`TLS negotiation: ${ tlsTime }`);
console.log(`Time to first byte: ${ ttfb }`);
console.log(`Download time: ${ dt }`);
console.log(`Response size: ${ transferSize }`);
console.log(`nextHopProtocol: ${ nextHopProtocol }`);
/*
* Copyright 2018 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*
*/
'use strict';
console.log('Starting');
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
console.log('ServiceWorker registration failed: ', err);
});
});
} else {
console.log('serviceWorker is not in navigator');
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sorry</title>
</head>
<h1>Sorry</h1>
<body>
<p>We could not find that page</p>
</body>
</html>
/*
* Copyright 2018 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*
*/
console.log('Starting service worker');
const version = "0.001";
const cacheName = `gcs-static-${version}`;
self.addEventListener('install', e => {
e.waitUntil(
caches.open(cacheName).then(cache => {
return cache.addAll([
`/`,
`/index2.html`,
`/main2.js`
])
.then(() => self.skipWaiting());
})
);
});
self.addEventListener('activate', event => {
event.waitUntil(self.clients.claim());
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.open(cacheName)
.then(cache => cache.match(event.request, {ignoreSearch: true}))
.then(response => {
return response || fetch(event.request);
})
);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment