Skip to content

Instantly share code, notes, and snippets.

What would you like to do?

Frontend Masters: AWS for Frontend Engineers

You should have the following completed on your computer before the workshop:

  • Install the AWS CLI.
  • Have Node.js installed on your system. (Recommended: Use nvm.)
    • Install yarn with brew install yarn.
  • Create an AWS account. (This will require a valid credit card.)
  • Create a Travis CI account. (This should be as simple as logging in via GitHub).

Follow-Along Guides: I made a set of visual follow-along guides that you can reference throughout the course.


  • Noted (Base): This is the base that you can clone and work with.
  • Noted (Live): This is the live version that I'm going to be working with.

Useful Links

S3 Bucket Policy

This might be helpful at some point.

    "Version": "2012-10-17",
    "Statement": [
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::YOUR_BUCKET_NAME_HERE/*"

Travis Configuration

language: node_js
  - '8'
  yarn: true
    - node_modules
  - yarn test
  - yarn global add travis-ci-cloudfront-invalidation
  - yarn run build
  provider: s3
  access_key_id: $AWS_ACCESS_KEY_ID
  secret_access_key: $AWS_SECRET_ACCESS_KEY
  bucket: $S3_BUCKET
  skip_cleanup: true
  local-dir: dist
    branch: master
  - travis-ci-cloudfront-invalidation -a $AWS_ACCESS_KEY_ID -s $AWS_SECRET_ACCESS_KEY -c $CLOUDFRONT_ID -i '/*' -b $TRAVIS_BRANCH -p $TRAVIS_PULL_REQUEST

Redirecting to index.html on Valid Client-side Routes

Viewer Request

exports.handler = (event, context, callback) => {
  const request = event.Records[0].cf.request;

  console.log('Before', request.uri);

  if (/notes\/\d(\/edit)?/.test(request.uri)) {
    request.uri = '/index.html'

  console.log('After', request.uri);

  callback(null, request);

Implementing Security Headers

Viewer Response

'use strict';

exports.handler = (event, context, callback) => {
  const response = event.Records[0].cf.response;

  response.headers['strict-transport-security'] = [{
    key: 'Strict-Transport-Security',
    value: 'max-age=31536000; includeSubDomains'

  response.headers['content-security-policy'] = [{
    key: 'Content-Security-Policy',
    value: "default-src 'self'"

  response.headers['x-xss-protection'] = [{
    key: 'X-XSS-Protection',
    value: '1; mode=block'

  response.headers['x-content-type-options'] = [{
    key: 'X-Content-Type-Options',
    value: 'nosniff'

  response.headers['x-frame-options'] = [{
    key: 'X-Frame-Options',
    value: 'DENY'

  callback(null, response);

Implementing a Redirect

Origin Request

exports.handler = (event, context, callback) => {
  const request = event.Records[0].cf.request;

  const response = {
    status: '302',
    statusDescription: 'Found',
    headers: {
      location: [{
        key: 'Location',
        value: ''

  if (request.uri === '/secret') {
    console.log('Got it');
    return callback(null, response);

  callback(null, request);

This comment has been minimized.

Copy link

@laniehei laniehei commented Aug 15, 2018

Users on Windows: when downloading the AWS CLI, ensure that you are scrolling down in the documentation, and following the specific "for Windows" installation guide so that you don't have a bad time.


This comment has been minimized.

Copy link

@Whatapalaver Whatapalaver commented Jun 9, 2019

I seem to have been unable to force AWS to use the us_east-1 region
If you are in a similar boat it's worth noting that travis ci assumes this region and builds will fail as a result.
It's an easy fix, just add the region into the deploy section of the travis.yml or use another ENV variable.
For me this looks like:
region: eu-west-2


This comment has been minimized.

Copy link

@Miteshdv Miteshdv commented Dec 4, 2020

It Seems AWS has made this complete solution easy , by using Cloud Formation and AWS Static Site Storage

Please Confirm if this solution is as good as yours explained in Course


This comment has been minimized.

Copy link
Owner Author

@stevekinney stevekinney commented Dec 28, 2020

They both accomplish the same thing. So, whatever is more appealing to you should work.


This comment has been minimized.

Copy link

@krfong916 krfong916 commented Feb 22, 2021

Hi Steve, how would you set CSP headers for this configuration (caching a static SPA), while using a 3rd party component library? Given that inline styles will be injected to the document at runtime and not at build time, from your view, how would one handle this? What would a meta tag look like?

I would most like to hear your thoughts. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment