Static Websites
on AWS
w/ Nuxt
Michael Cole
Freelancer
Poverty Abolitionist
michael@CoalitionToAbolishPoverty.org
Static Website on AWS w/ Nuxt
What is the difference between 'nuxt build'
and 'nuxt generate' ?
What is a web app?
- Browse to URL
- Login
- Do stuff
- Come back later
- Remembers yer stuff
What is a static site?
- Browse to URL
- Click around
- Read stuff
- Written using declarative content (markdown, not html)
Web Tech Tree
GET URL: server process
GET URL: file
Dynamic Page (PHP, Nuxt Universal)
Static Page (jQuery)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5913394/web1.gif)
Static Page
<html>
<body>
stuff
</body>
</html>
Serve content from files.
Apache, Nginx
Dynamic Page
<html>
<body>
<?php echo "stuff"; ?>
</body>
</html>
Serve content from URL, templates, and cookies.
CGI, PHP, Ruby on Rails, WordPress
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5913188/web1.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5913189/mysql-php.png)
Web Tech Tree
Dynamic Page (PHP, Nuxt Universal)
Single Page App (Nuxt SPA)
Static Site Gen (Github pages)
Static Page (jQuery)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5913394/web1.gif)
GET URL: server process
Click 'page': browser
GET URL: file
Click 'page': file
SPA
<html>
<body>
<a ui-sref="stuff">x</a>
</body>
</html>
One HTML for all URLS. Browser routes "pages".
Angular + ui-router, Backbone, Ember, Vue
Static Site
index.html
blog/hello.html
blog/post-more-often.html
about-us/index.html
contact/index.html
Prerender HTML for each URL w/ declarative content.
Jekyll, Wintersmith, Hexo, Github pages, Nuxt
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5913244/download.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5914262/logo-2x.png)
Web Tech Tree
Dynamic Page (PHP, Nuxt Universal)
Single Page App (Nuxt SPA)
Static Site Gen (Github pages)
Static Page (jQuery)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5913394/web1.gif)
GET URL: server process
Click 'page': browser
GET URL: file
Click 'page': file
!!!
Web Tech Tree
Dynamic Page (PHP, Nuxt Universal)
Single Page App (Nuxt SPA)
Static Site Gen (Github pages)
Static Page (jQuery)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5913394/web1.gif)
GET URL: server process
Click 'page': browser
! Not SEO friendly !
GET URL: file
Click 'page': file
! Not an App !
!!!
nuxt static site (pre-rendered)
By default, Vue components produce and manipulate DOM in the browser as output.
However, it is also possible to:
- render the same components into HTML strings on the server (e.g. as files)
- send them directly to the browser
- finally "hydrate" the static markup into a fully interactive app on the client (asyncData)
This is a Vue.js feature:
Web Tech Tree
Statically Generated SPA (Nuxt Static Site)
Progressive Web App (Offline, Nuxt PWA)
GET URL: file
Click page: browser
"Hydrate": REST (asyncData)
Dynamic Page (PHP, Nuxt Universal)
Single Page App (Nuxt SPA)
Static Site Gen (Github pages)
Static Page (jQuery)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5913394/web1.gif)
nuxt generate
- Transpile declarative content
- into a
Nuxt Universal/Vue SPA
Then:
- pre-render with Vue SSR
- into a
Nuxt Static Site
nuxt build
- Transpile declarative content
- into a Nuxt Universal/Vue SPA
code & content
/layouts
/pages
/assets
/components
/plugins
Nuxt Universal App (Express)
/.nuxt/index.js
Static Site
/index.html
/pages/index.html
/_nuxt/*
nuxt build
(aka Server Rendered Deployment) (aka Nuxt Universal)
// ------------------------- Dev:
nuxt
// or
npm run dev
// ------------------------- Production:
// compile to .nuxt directory
nuxt build
// run web server with Node.js
nuxt start
nuxt build -> .nuxt
GET page: server process
Click page: browser
// ------------------------- Dev:
nuxt || npm run dev
// ------------------------- Production:
// compile static site to dist directory
nuxt generate
// Push files to web server...
nuxt_deploy_or_whatever.sh
nuxt generate
(aka Server Generated Deployment) (pre-rendered) (static site)
nuxt generate -> dist
GET page: file
Click page: browser
"Hydrate": REST (asyncData)
Static Websites
on AWS
w/ Nuxt
nuxt deploy? Nope.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5918437/gold.png)
Laptop:
`nuxt generate`
?
Cloud:
?
nuxt deploy? Nope.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5918437/gold.png)
Laptop:
`nuxt generate`
Why Cloud?
- More secure. No server to p0wn.
- Faster page loads. CDN
- Free SSL cert.
- Cheaper hosting. $0.84 /month
Why AWS?
I already have an account.
Gulp code from wintersmith project
nuxt on AWS w/ S3 + Cloudfront
SSL
CDN
Browser
S3 bucket
CloudFront
npm run deploy
nuxt generate
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5918437/gold.png)
deploy.sh
gulp
AWS
Laptop
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5918437/gold.png)
AWS: S3 + Cloudfront
SSL
CDN
Browser
S3 bucket
CloudFront
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5918437/gold.png)
Stores files
Static website hosting
http://coalitiontoabolishpoverty.org.s3-website-us-east-1.amazonaws.com
Content Delivery Network
Certificate Manager
"Invalidate" cache to refresh from S3.
https://coalitiontoabolishpoverty.org
nuxt on AWS w/ S3 + Cloudfront
SSL
CDN
Browser
S3 bucket
CloudFront
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5918437/gold.png)
Setup AWS stuff:
1. S3 bucket
2. CloudFront -> S3
3. DNS -> CloudFront
4. Securely push files -> S3
Deploy our stuff:
1. Push files -> S3
2. Invalidate CloudFront to refresh from S3
nuxt on AWS w/ S3 + Cloudfront
Setup AWS stuff:
1. S3 bucket
2. CloudFront -> S3
3. DNS -> CloudFront
4. Securely push files -> S3
SSL
CDN
Browser
S3 bucket
CloudFront
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5918437/gold.png)
Read the Nuxt FAQ
nuxt on AWS w/ S3 + Cloudfront
AWS Console tour
- S3 Bucket
- Static Site (AWS_BUCKET_NAME)
- Bucket Policy (Public)
- IAM User Policy
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- CloudFront
- AWS_CLOUDFRONT (uppercase)
- xxxx.cloudfront.net
- DNS ALIAS or CNAME
SSL
CDN
Browser
S3 bucket
CloudFront
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5918437/gold.png)
nuxt on AWS w/ S3 + Cloudfront
Setup AWS stuff:
1. S3 bucket
2. CloudFront -> S3
3. DNS -> CloudFront
4. Securely push files -> S3
We need this data:
- AWS_BUCKET_NAME="example.com"
- AWS_CLOUDFRONT="UPPERCASE"
- AWS_ACCESS_KEY_ID="key"
- AWS_SECRET_ACCESS_KEY="secret"
Read the Nuxt FAQ
SSL
CDN
Browser
S3 bucket
CloudFront
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5918437/gold.png)
nuxt on AWS w/ S3 + Cloudfront
Deploy our stuff:
1. Push files -> S3
2. Invalidate CloudFront to refresh cache
(see new content right away)
gulp - streaming build system w/o a webpack PhD
Problem solved w/ NPM packages:
- gulp
- gulp-awspublish
- gulp-cloudfront-invalidate-aws-publish
- concurrent-transform (parallel uploads)
SSL
CDN
Browser
S3 bucket
CloudFront
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5918437/gold.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/361097/images/5919351/gulp-2x.png)
Deploy our stuff:
1. deploy.sh
2. .gitignore
3. packages!
4. gulpfile.js
5. Deploy already!
#!/bin/bash
export AWS_ACCESS_KEY_ID="key"
export AWS_SECRET_ACCESS_KEY="secret"
export AWS_BUCKET_NAME="example.com"
export AWS_CLOUDFRONT="UPPERCASE"
# If nvm (node version manager),
[ -s "$HOME/.nvm/nvm.sh" ] \
&& source "$HOME/.nvm/nvm.sh" \
&& nvm use
# Npm install if not already.
[ ! -d "node_modules" ] && npm install
nuxt generate
gulp deploy
# or configure package.json scripts
npm run generate
npm run deploy
Deploy our stuff:
1. deploy.sh
2. .gitignore
3. packages!
4. gulpfile.js
5. Deploy already!
$ chmod +x deploy.sh
$ echo "
# Don't commit build files
node_modules
dist
.nuxt
.awspublish
deploy.sh
" >> .gitignore
Deploy our stuff:
1. deploy.sh
2. .gitignore
3. packages!
4. gulpfile.js
5. Deploy already!
$ npm install --save-dev \
gulp \
gulp-awspublish \
gulp-cloudfront-invalidate-aws-publish \
concurrent-transform
$ npm install -g gulp
4. gulpfile.js
var gulp = require('gulp');
var awspublish = require('gulp-awspublish');
var cloudfront = require('gulp-cloudfront-invalidate-aws-publish');
var parallelize = require('concurrent-transform');
// https://docs.aws.amazon.com/cli/latest/userguide/cli-environment.html
var config = {
// Required
params: { Bucket: process.env.AWS_BUCKET_NAME },
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
// Optional
deleteOldVersions: false, // NOT FOR PRODUCTION
distribution: process.env.AWS_CLOUDFRONT, // CloudFront distribution ID
region: process.env.AWS_DEFAULT_REGION,
headers: { /*'Cache-Control': 'max-age=315360000, no-transform, public',*/ },
// Sensible Defaults - gitignore these Files and Dirs
distDir: 'dist',
indexRootPath: true,
cacheFileName: '.awspublish',
concurrentUploads: 10,
wait: true, // wait for CloudFront invalidation to complete (about 30-60 seconds)
}
gulp.task('deploy', function() {
// create a new publisher using S3 options
// http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#constructor-property
var publisher = awspublish.create(config, config);
// Collect all the files in distDir
var g = gulp.src('./' + config.distDir + '/**');
// Send those files through publish.
g = g.pipe(parallelize(publisher.publish(config.headers), config.concurrentUploads))
// Invalidate distribution cache so new content appears
if (config.distribution) {
console.log('Configured with CloudFront distribution');
g = g.pipe(cloudfront(config));
} else {
console.log('No CloudFront distribution configured - skipping CDN invalidation');
}
// Delete removed files - breaks older versions being viewed
if (config.deleteOldVersions) g = g.pipe(publisher.sync());
// create a local cache of files to speed up consecutive uploads
g = g.pipe(publisher.cache());
// print upload updates to console
g = g.pipe(awspublish.reporter());
return g;
});
$ ./deploy.sh
Found '/home/michael/scm/example.com/www/.nvmrc' with version <8>
Now using node v8.11.2 (npm v5.6.0)
> example.com@1.0.0 generate /home/michael/scm/example.com/www
> nuxt generate
nuxt:generate Generating... +0ms
nuxt:build App root: /home/michael/scm/example.com/www +0ms
nuxt:build Generating /home/michael/scm/example.com/www/.nuxt files... +0ms
nuxt:build Generating files... +36ms
nuxt:build Generating routes... +10ms
nuxt:build Building files... +24ms
████████████████████ 100%
Build completed in 7.009s
DONE Compiled successfully in 7013ms 21:25:22
Hash: 421d017116d2d95dd1e3
Version: webpack 3.12.0
Time: 7013ms
Asset Size Chunks Chunk Names
pages/index.ef923f795c1cecc9a444.js 10.6 kB 0 [emitted] pages/index
layouts/default.87a49937c330bdd31953.js 2.69 kB 1 [emitted] layouts/default
pages/our-values.f60c731d5c3081769fd9.js 3.03 kB 2 [emitted] pages/our-values
pages/join-us.835077c4e6b55ed1bba4.js 1.3 kB 3 [emitted] pages/join-us
pages/how.75f8cb5bc24e38bca3b3.js 2.59 kB 4 [emitted] pages/how
app.6dbffe6ac4383bd30a92.js 202 kB 5 [emitted] app
vendor.134043c361c9ad199c6d.js 6.31 kB 6 [emitted] vendor
manifest.421d017116d2d95dd1e3.js 1.59 kB 7 [emitted] manifest
+ 3 hidden assets
Hash: 9fd206f4b4e571e9571f
Version: webpack 3.12.0
Time: 2239ms
Asset Size Chunks Chunk Names
server-bundle.json 306 kB [emitted]
nuxt: Call generate:distRemoved hooks (1) +0ms
nuxt:generate Destination folder cleaned +10s
nuxt: Call generate:distCopied hooks (1) +8ms
nuxt:generate Static & build files copied +7ms
nuxt:render Rendering url /our-values +0ms
nuxt:render Rendering url /how +67ms
nuxt:render Rendering url /join-us +1ms
nuxt:render Rendering url / +0ms
nuxt: Call generate:page hooks (1) +913ms
nuxt: Call generate:page hooks (1) +205ms
nuxt: Call generate:page hooks (1) +329ms
nuxt: Call generate:page hooks (1) +361ms
nuxt:generate Generate file: /our-values/index.html +2s
nuxt:generate Generate file: /how/index.html +0ms
nuxt:generate Generate file: /join-us/index.html +0ms
nuxt:generate Generate file: /index.html +0ms
nuxt:render Rendering url / +2s
nuxt: Call generate:done hooks (1) +4ms
nuxt:generate HTML Files generated in 11.8s +5ms
nuxt:generate Generate done +0ms
[21:25:27] Using gulpfile ~/scm/example.com/www/gulpfile.js
[21:25:27] Starting 'deploy'...
Configured with CloudFront distribution
[21:25:27] [cache] README.md
[21:25:27] [cache] android-chrome-192x192.png
[21:25:27] [cache] android-chrome-512x512.png
[21:25:27] [cache] apple-touch-icon.png
[21:25:27] [cache] browserconfig.xml
[21:25:27] [cache] favicon-16x16.png
[21:25:27] [cache] favicon-32x32.png
[21:25:27] [cache] favicon.ico
[21:25:27] [cache] favicon.svg
[21:25:27] [cache] logo-branches.svg
[21:25:27] [cache] logo-small.svg
[21:25:27] [cache] logo.svg
[21:25:27] [cache] mstile-150x150.png
[21:25:27] [cache] og-image.jpg
[21:25:27] [cache] safari-pinned-tab.svg
[21:25:27] [cache] site.webmanifest
[21:25:28] [create] _nuxt/manifest.421d017116d2d95dd1e3.js
[21:25:29] [update] 200.html
[21:25:30] [create] videos/flag.jpg
[21:25:30] [create] _nuxt/vendor.134043c361c9ad199c6d.js
[21:25:34] [create] videos/flag.mp4
[21:25:34] [cache] _nuxt/pages/how.75f8cb5bc24e38bca3b3.js
[21:25:34] [cache] _nuxt/pages/join-us.835077c4e6b55ed1bba4.js
[21:25:34] [cache] _nuxt/pages/our-values.f60c731d5c3081769fd9.js
[21:25:36] [update] our-values/index.html
[21:25:36] [create] _nuxt/layouts/default.87a49937c330bdd31953.js
[21:25:36] [create] _nuxt/app.6dbffe6ac4383bd30a92.js
[21:25:37] [create] _nuxt/pages/index.ef923f795c1cecc9a444.js
[21:25:38] [update] join-us/index.html
[21:25:38] [update] how/index.html
[21:25:43] [create] videos/flag.webm
[21:25:43] [update] index.html
[21:25:43] CloudFront invalidation created: I16NXXXXX4JDOA
[21:26:09] Finished 'deploy' after 42 s
5. Deploy Already!
michael@CoalitionToAbolishPoverty.org
Static Websites on AWS w/ Nuxt
By michaelcole
Static Websites on AWS w/ Nuxt
- 1,007