DEV Community

Discussion on: Help serving assets over HTTP/2 for a Gatsby Netlify hosted site

Collapse
 
nickytonline profile image
Nick Taylor

It's apparently a bug with Chrome and Lighthouse.

ServiceWorker serving cache in HTTP/1.1 protocol #11123

rayriffy avatar
rayriffy commented on Jan 17, 2019

Description

I deployed my prodution and staging site on Netlify and I got reports from Lighthouse audits that some my resources are being served in HTTP/1.1 protocol which it shouldn't do that.

I found out later that all of the resurces that being served in HTTP/1.1 are from ServiceWorker. I think there's some issue with gatsby-plugin-offine

Steps to reproduce

  1. Open DevTools with Network tab
  2. Go to blog-staging.rayriffy.com. From this point you can see all resources are downloaded in HTTP/2 protocol
  3. Refresh page. And you should see cached data from ServiceWorker are served in HTTP/1.1

Expected result

ServiceWorker should serve cache in HTTP/2 protocol

Actual result

It served in HTTP/1.1 protocol

Environment

  System:
    OS: Windows 10
    CPU: x64 Intel(R) Core(TM) i7-5500U CPU @ 2.40GHz
  Binaries:
    Yarn: 1.13.0 - C:\Program Files (x86)\Yarn\bin\yarn.CMD
    npm: 6.5.0 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: 44.17763.1.0
  npmPackages:
    gatsby: ^2.0.91 => 2.0.91
    gatsby-image: ^2.0.26 => 2.0.26
    gatsby-paginate: ^1.0.16 => 1.0.16
    gatsby-plugin-feed: ^2.0.8 => 2.0.11
    gatsby-plugin-google-analytics: ^2.0.9 => 2.0.9
    gatsby-plugin-google-fonts: ^0.0.4 => 0.0.4
    gatsby-plugin-manifest: ^2.0.13 => 2.0.13
    gatsby-plugin-netlify: ^2.0.5 => 2.0.6
    gatsby-plugin-netlify-cache: ^1.0.0 => 1.0.0
    gatsby-plugin-offline: ^2.0.21 => 2.0.21
    gatsby-plugin-react-helmet: ^3.0.5 => 3.0.5
    gatsby-plugin-robots-txt: ^1.3.0 => 1.3.0
    gatsby-plugin-sharp: ^2.0.17 => 2.0.17
    gatsby-plugin-sitemap: ^2.0.4 => 2.0.4
    gatsby-plugin-typography: ^2.2.5 => 2.2.5
    gatsby-remark-copy-linked-files: ^2.0.8 => 2.0.8
    gatsby-remark-embed-gist: ^1.1.5 => 1.1.5
    gatsby-remark-embed-spotify: ^2.0.2 => 2.0.2
    gatsby-remark-images: ^3.0.1 => 3.0.1
    gatsby-remark-prismjs: ^3.2.0 => 3.2.0
    gatsby-remark-responsive-iframe: ^2.0.8 => 2.0.8
    gatsby-remark-smartypants: ^2.0.5 => 2.0.7
    gatsby-source-filesystem: ^2.0.16 => 2.0.16
    gatsby-transformer-json: ^2.1.7 => 2.1.7
    gatsby-transformer-remark: ^2.2.0 => 2.2.0
    gatsby-transformer-sharp: ^2.1.10 => 2.1.10

error The system cannot find the path specified.

gatsby-config.js

Source
var hostname

if (process.env.GATSBY_ENV === 'production') {
  hostname = 'https://blog.rayriffy.com'
} else if (process.env.GATSBY_ENV === 'staging') {
  hostname = 'https://blog-staging.rayriffy.com'
} else if (process.env.GATSBY_ENV === 'development') {
  hostname = 'https://localhost:8000'
}

module.exports = {
  siteMetadata: {
    title: 'Riffy Blog',
    author: 'Phumrapee Limpianchop',
    description: 'The Nerdy Blogger',
    siteUrl: `${hostname}`,
  },
  pathPrefix: '/',
  plugins: [
    {
      resolve: `gatsby-plugin-google-fonts`,
      options: {
        fonts: [`kanit`],
      },
    },
    `gatsby-plugin-netlify-cache`,
    `gatsby-transformer-json`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `./src/assets/database`,
      },
    },
    {
      resolve: 'gatsby-plugin-robots-txt',
      options: {
        resolveEnv: () => process.env.GATSBY_ENV,
        env: {
          production: {
            policy: [
              {
                userAgent: '*',
                disallow: ['/pages', '/category', '/author'],
              },
            ],
          },
          staging: {
            policy: [
              {
                userAgent: '*',
                disallow: ['/'],
              },
            ],
          },
          development: {
            policy: [
              {
                userAgent: '*',
                disallow: ['/'],
              },
            ],
          },
        },
      },
    },
    {
      resolve: `gatsby-plugin-netlify`,
      options: {
        headers: {
          '/feed.json': ['Access-Control-Allow-Origin: *'],
        },
      },
    },
    {
      resolve: `gatsby-plugin-sitemap`,
      options: {
        output: `/sitemap.xml`,
        exclude: [
          '/pages/*',
          '/category',
          '/category/*',
          '/author',
          '/author/*',
        ],
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/pages`,
        name: 'pages',
        ignore: [`**/.*`],
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/assets`,
        name: 'assets',
        ignore: [`**/.*`],
      },
    },
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          'gatsby-remark-embed-spotify',
          'riffy-gjs-embeded-video',
          {
            resolve: 'gatsby-remark-embed-gist',
            options: {
              username: 'rayriffy',
              includeDefaultCss: true,
            },
          },
          {
            resolve: `gatsby-remark-images`,
            options: {
              maxWidth: 1000,
              linkImagesToOriginal: false,
              sizeByPixelDensity: true,
              withWebp: true,
              quality: 80,
            },
          },
          'gatsby-remark-responsive-iframe',
          'gatsby-remark-prismjs',
          'gatsby-remark-copy-linked-files',
          'gatsby-remark-smartypants',
        ],
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-google-analytics`,
      options: {
        trackingId: `${
          process.env.GATSBY_ENV === 'production'
            ? 'UA-85367836-2'
            : process.env.GATSBY_ENV === 'staging'
            ? 'UA-85367836-3'
            : ''
        }`,
      },
    },
    `gatsby-plugin-feed`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `Riffy Blog`,
        short_name: `Riffy Blog`,
        start_url: `/`,
        background_color: `#f5f5f5`,
        theme_color: `#1e88e5`,
        display: `minimal-ui`,
        icon: `src/assets/logo.png`,
      },
    },
    {
      resolve: `gatsby-plugin-offline`,
      options: {
        dontCacheBustUrlsMatching: /(\.js$|\.css$|\/static\/)/,
        runtimeCaching: [
          {
            urlPattern: /(\.js$|\.css$|\/static\/)/,
            handler: `cacheFirst`,
          },
          {
            urlPattern: /^https?:\/\/(www\.blog.rayriffy\.com|localhost:8000|localhost:9000|blog-staging\.rayriffy\.com|blog\.rayriffy\.com).*\.(png|jpg|jpeg|webp|svg|gif|tiff|js|woff|woff2|json|css)$/,
            handler: `staleWhileRevalidate`,
          },
          {
            urlPattern: /^https?:\/\/fonts\.googleapis\.com\/css/,
            handler: `staleWhileRevalidate`,
          },
        ],
      },
    },
    `gatsby-plugin-react-helmet`,
    {
      resolve: 'gatsby-plugin-typography',
      options: {
        pathToConfigModule: 'src/utils/typography',
        omitGoogleFont: true,
      },
    },
  ],
}

package.json

Source
{
  "name": "rayriffy-blog",
  "description": "The Nerdy Blogger",
  "version": "1.0.10",
  "author": "Phumrapee Limpianchop <contact@rayriffy.com>",
  "bugs": {
    "url": "https://github.com/rayriffy/rayriffy-blog/issues"
  },
  "dependencies": {
    "cross-env": "^5.2.0",
    "gatsby": "^2.0.91",
    "gatsby-image": "^2.0.26",
    "gatsby-paginate": "^1.0.16",
    "gatsby-plugin-feed": "^2.0.8",
    "gatsby-plugin-google-analytics": "^2.0.9",
    "gatsby-plugin-google-fonts": "^0.0.4",
    "gatsby-plugin-manifest": "^2.0.13",
    "gatsby-plugin-netlify": "^2.0.5",
    "gatsby-plugin-netlify-cache": "^1.0.0",
    "gatsby-plugin-offline": "^2.0.21",
    "gatsby-plugin-react-helmet": "^3.0.5",
    "gatsby-plugin-robots-txt": "^1.3.0",
    "gatsby-plugin-sharp": "^2.0.17",
    "gatsby-plugin-sitemap": "^2.0.4",
    "gatsby-plugin-typography": "^2.2.5",
    "gatsby-remark-copy-linked-files": "^2.0.8",
    "gatsby-remark-embed-gist": "^1.1.5",
    "gatsby-remark-embed-spotify": "^2.0.2",
    "gatsby-remark-images": "^3.0.1",
    "gatsby-remark-prismjs": "^3.2.0",
    "gatsby-remark-responsive-iframe": "^2.0.8",
    "gatsby-remark-smartypants": "^2.0.5",
    "gatsby-source-filesystem": "^2.0.16",
    "gatsby-transformer-json": "^2.1.7",
    "gatsby-transformer-remark": "^2.2.0",
    "gatsby-transformer-sharp": "^2.1.10",
    "lodash": "^4.17.11",
    "prismjs": "^1.15.0",
    "prop-types": "^15.6.2",
    "react": "^16.7.0",
    "react-adsense": "^0.0.6",
    "react-dom": "^16.7.0",
    "react-helmet": "^5.2.0",
    "react-icons": "^3.3.0",
    "react-typography": "^0.16.18",
    "riffy-gjs-embeded-video": "^1.3.1",
    "typeface-merriweather": "0.0.54",
    "typeface-montserrat": "0.0.43",
    "typography": "^0.16.17"
  },
  "devDependencies": {
    "babel-eslint": "^10.0.1",
    "eslint": "^5.12.0",
    "eslint-config-prettier": "^3.5.0",
    "eslint-config-standard": "^12.0.0",
    "eslint-plugin-import": "^2.14.0",
    "eslint-plugin-node": "^8.0.1",
    "eslint-plugin-prettier": "^3.0.1",
    "eslint-plugin-promise": "^4.0.1",
    "eslint-plugin-react": "^7.12.3",
    "eslint-plugin-standard": "^4.0.0",
    "prettier": "^1.14.2"
  },
  "homepage": "https://blog.rayriffy.com",
  "keywords": [
    "gatsby",
    "gatsbyjs",
    "react",
    "reactjs",
    "es6",
    "blog"
  ],
  "license": "MIT",
  "main": "n/a",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/rayriffy/rayriffy-blog.git"
  },
  "scripts": {
    "dev": "cross-env GATSBY_ENV=development gatsby develop --https",
    "dev-staging": "cross-env GATSBY_ENV=staging gatsby develop --https",
    "dev-prod": "cross-env GATSBY_ENV=production gatsby develop --https",
    "lint": "./node_modules/.bin/eslint --ext .js,.jsx --ignore-pattern public .",
    "test": "echo \"Error: no test specified\" && exit 1",
    "format": "prettier --trailing-comma es6 --no-semi --single-quote --write 'src/**/*.js' 'src/**/*.md'",
    "develop": "cross-env GATSBY_ENV=development gatsby develop --https",
    "start": "npm run develop",
    "build": "cross-env GATSBY_ENV=production gatsby build",
    "build-staging": "cross-env GATSBY_ENV=staging gatsby build",
    "deploy": "gatsby build --prefix-paths",
    "fix-semi": "eslint --quiet --ignore-pattern node_modules --ignore-pattern public --parser babel-eslint --no-eslintrc --rule '{\"semi\": [2, \"never\"], \"no-extra-semi\": [2]}' --fix gatsby-node.js"
  }
}

gatsby-node.js

Source
const _ = require('lodash')
const Promise = require('bluebird')
const fs = require('fs')
const path = require('path')
const {createFilePath} = require('gatsby-source-filesystem')

exports.createPages = ({graphql, actions}) => {
  const {createPage} = actions

  var siteUrl

  return new Promise((resolve, reject) => {
    resolve(
      graphql(
        `
          {
            site {
              siteMetadata {
                siteUrl
              }
            }
            allMarkdownRemark(
              sort: {fields: [frontmatter___date], order: DESC}
            ) {
              edges {
                node {
                  fields {
                    slug
                  }
                  frontmatter {
                    title
                    subtitle
                    status
                    author
                  }
                }
              }
            }
            allCategoriesJson {
              edges {
                node {
                  key
                  name
                  desc
                }
              }
            }
            allAuthorsJson {
              edges {
                node {
                  user
                }
              }
            }
            lifestyle: allMarkdownRemark(
              filter: {frontmatter: {category: {regex: "/lifestyle/"}}}
            ) {
              edges {
                node {
                  frontmatter {
                    status
                  }
                }
              }
            }
            misc: allMarkdownRemark(
              filter: {frontmatter: {category: {regex: "/misc/"}}}
            ) {
              edges {
                node {
                  frontmatter {
                    status
                  }
                }
              }
            }
            music: allMarkdownRemark(
              filter: {frontmatter: {category: {regex: "/music/"}}}
            ) {
              edges {
                node {
                  frontmatter {
                    status
                  }
                }
              }
            }
            programming: allMarkdownRemark(
              filter: {frontmatter: {category: {regex: "/programming/"}}}
            ) {
              edges {
                node {
                  frontmatter {
                    status
                  }
                }
              }
            }
            review: allMarkdownRemark(
              filter: {frontmatter: {category: {regex: "/review/"}}}
            ) {
              edges {
                node {
                  frontmatter {
                    status
                  }
                }
              }
            }
            tutorial: allMarkdownRemark(
              filter: {frontmatter: {category: {regex: "/tutorial/"}}}
            ) {
              edges {
                node {
                  frontmatter {
                    status
                  }
                }
              }
            }
            rayriffy: allMarkdownRemark(
              filter: {frontmatter: {author: {regex: "/rayriffy/"}}}
            ) {
              edges {
                node {
                  frontmatter {
                    status
                  }
                }
              }
            }
            SiriuSStarS: allMarkdownRemark(
              filter: {frontmatter: {author: {regex: "/SiriuSStarS/"}}}
            ) {
              edges {
                node {
                  frontmatter {
                    status
                  }
                }
              }
            }
          }
        `,
      )
        .then(result => {
          siteUrl = result.data.site.siteMetadata.siteUrl
          var filteredresult
          if (
            process.env.GATSBY_ENV === 'production' ||
            process.env.GATSBY_ENV === 'staging'
          ) {
            filteredresult = {
              data: {
                allMarkdownRemark: {edges: null},
                allCategoriesJson: {edges: null},
                allAuthorsJson: {edges: null},
                lifestyle: {edges: null},
                misc: {edges: null},
                music: {edges: null},
                programming: {edges: null},
                review: {edges: null},
                tutorial: {edges: null},
                rayriffy: {edges: null},
                SiriuSStarS: {edges: null},
              },
            }
            filteredresult.data.allMarkdownRemark.edges = result.data.allMarkdownRemark.edges.filter(
              a => a.node.frontmatter.status === 'published',
            )
            filteredresult.data.lifestyle.edges = result.data.lifestyle.edges.filter(
              a => a.node.frontmatter.status === 'published',
            )
            filteredresult.data.misc.edges = result.data.misc.edges.filter(
              a => a.node.frontmatter.status === 'published',
            )
            filteredresult.data.music.edges = result.data.music.edges.filter(
              a => a.node.frontmatter.status === 'published',
            )
            filteredresult.data.programming.edges = result.data.programming.edges.filter(
              a => a.node.frontmatter.status === 'published',
            )
            filteredresult.data.review.edges = result.data.review.edges.filter(
              a => a.node.frontmatter.status === 'published',
            )
            filteredresult.data.tutorial.edges = result.data.tutorial.edges.filter(
              a => a.node.frontmatter.status === 'published',
            )
            filteredresult.data.rayriffy.edges = result.data.rayriffy.edges.filter(
              a => a.node.frontmatter.status === 'published',
            )
            filteredresult.data.SiriuSStarS.edges = result.data.SiriuSStarS.edges.filter(
              a => a.node.frontmatter.status === 'published',
            )
            filteredresult.data.allCategoriesJson.edges =
              result.data.allCategoriesJson.edges
            filteredresult.data.allAuthorsJson.edges =
              result.data.allAuthorsJson.edges
          } else if (process.env.GATSBY_ENV === 'development') {
            filteredresult = result
          }
          return filteredresult
        })
        .then(result => {
          if (result.errors) {
            console.error(result.errors)
            reject(result.errors)
          }

          const posts = result.data.allMarkdownRemark.edges
          const catrgories = result.data.allCategoriesJson.edges
          const authors = result.data.allAuthorsJson.edges

          var filter
          const postsPerPage = 5
          if (
            process.env.GATSBY_ENV === 'production' ||
            process.env.GATSBY_ENV === 'staging'
          ) {
            filter = 'draft'
          } else if (process.env.GATSBY_ENV === 'development') {
            filter = ''
          }

          // Create blog lists pages.
          const numPages = Math.ceil(posts.length / postsPerPage)

          _.times(numPages, i => {
            createPage({
              path: i === 0 ? `/` : `/pages/${i + 1}`,
              component: path.resolve('./src/templates/blog-list.js'),
              context: {
                limit: postsPerPage,
                skip: i * postsPerPage,
                status: filter,
                numPages,
                currentPage: i + 1,
              },
            })
          })

          // Create blog posts pages.
          var count = 0
          var jsonFeed = []
          _.each(posts, (post, index) => {
            const previous =
              index === posts.length - 1 ? null : posts[index + 1].node
            const next = index === 0 ? null : posts[index - 1].node

            if (count < 5) {
              jsonFeed.push({
                name: post.node.frontmatter.title,
                desc: post.node.frontmatter.subtitle,
                slug: siteUrl + post.node.fields.slug,
              })
            }

            createPage({
              path: post.node.fields.slug,
              component: path.resolve('./src/templates/blog-post.js'),
              context: {
                author: post.node.frontmatter.author,
                slug: post.node.fields.slug,
                previous,
                next,
              },
            })
            count++
          })

          fs.writeFile('public/feed.json', JSON.stringify(jsonFeed), function(
            err,
          ) {
            if (err) {
              console.error(err)
              reject(err)
            }
          })

          // Create category pages
          var categoryPathPrefix = 'category/'
          _.each(catrgories, category => {
            var totalCount = result.data[category.node.key].edges.length
            var numCategoryPages = Math.ceil(totalCount / postsPerPage)
            var pathPrefix = categoryPathPrefix + category.node.key
            _.times(numCategoryPages, i => {
              createPage({
                path: i === 0 ? pathPrefix : pathPrefix + `/pages/${i + 1}`,
                component: path.resolve('./src/templates/category.js'),
                context: {
                  category: category.node.key,
                  currentPage: i + 1,
                  limit: postsPerPage,
                  numPages: numCategoryPages,
                  pathPrefix,
                  regex: '/' + category.node.key + '/',
                  skip: i * postsPerPage,
                  status: filter,
                },
              })
            })
          })

          // Create author pages
          var authorPathPrefix = 'author/'
          _.each(authors, author => {
            var totalCount = result.data[author.node.user].edges.length
            var numAuthorPages = Math.ceil(totalCount / postsPerPage)
            var pathPrefix = authorPathPrefix + author.node.user
            _.times(numAuthorPages, i => {
              createPage({
                path: i === 0 ? pathPrefix : pathPrefix + `/pages/${i + 1}`,
                component: path.resolve('./src/templates/author.js'),
                context: {
                  author: author.node.user,
                  currentPage: i + 1,
                  limit: postsPerPage,
                  numPages: numAuthorPages,
                  pathPrefix,
                  regex: '/' + author.node.user + '/',
                  skip: i * postsPerPage,
                  status: filter,
                },
              })
            })
          })
        }),
    )
  })
}

exports.onCreateNode = ({node, actions, getNode}) => {
  const {createNodeField} = actions

  if (node.internal.type === `MarkdownRemark`) {
    const value = createFilePath({node, getNode})
    createNodeField({
      name: `slug`,
      node,
      value,
    })
  }
}

PS. I will drop repository here github.com/rayriffy/rayriffy-blog

Thread Thread
 
darcyrayner profile image
Darcy Rayner

It happens in Firefox as well. I think that bug is probably in that gatsby plugin. I've seen service workers use HTTP 2 for loading content before.