My Nuxt project has around 700-1000 static and dynamic pages hosted via Netlify. ~300 of them are generated correctly.
In production, I discovered that certain dynamically generates routes get a HTTP 404 status code. However, they are generated and loaded without any other error in production. They just get the 404 error code which has bad implications for SEO. Dev server and running the generated dist folder via nuxt start locally both show no 404 code on those pages. Static generated pages work fine.
Here are my settings for nuxt.config.js (see full list below)
target: 'static',
generate: {
fallback: true,
....
}
I do not specify the dynamic routes on generate() because of the nuxt crawler .
package.json:
{
"name": "xxxxxxx",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate",
"generate_coin_list_file": "node scripts/generate_coin_list.js",
"generate_coin_images": "node scripts/download_coin_images.js"
},
"dependencies": {
"@nuxt/content": "^1.14.0",
"@nuxtjs/axios": "^5.13.6",
"@nuxtjs/gtm": "^2.4.0",
"@nuxtjs/proxy": "^2.1.0",
"@nuxtjs/sitemap": "^2.4.0",
"core-js": "^3.15.1",
"frontmatter-markdown-loader": "^3.7.0",
"ipx": "^0.9.4",
"lite-youtube-embed": "^0.2.0",
"nuxt": "^2.15.8",
"nuxt-jsonld": "^1.5.3",
"v-click-outside": "^3.2.0",
"vue-disqus": "^4.0.1"
},
"devDependencies": {
"@nuxt/image": "^0.6.2",
"@nuxtjs/tailwindcss": "^4.2.0",
"eslint-config-prettier": "^8.3.0",
"postcss": "^8.3.5",
"prettier": "^2.3.2"
}
}
I am not sure where to look for since this error does not seem to follow a pattern. Because I can generate it locally without the error it could be problem with Netlify. What do you guys think of this? Thank you!
Example: _slug.vue
export default {
name: 'BlogSlug',
async asyncData({ $content, params, route, error }) {
let author = {}
let article = await $content('articles', { deep: true })
.where({
slug: params.slug,
})
.fetch()
.catch(() => {
error({ statusCode: 404, message: 'Page not found' })
})
article = article[0]
const allArticles = await $content('articles', {
deep: true,
})
.sortBy('date', 'desc')
.limit(6)
.fetch()
.catch(() => {
error({ statusCode: 404, message: 'Page not found' })
})
if (article.author) {
author = await $content('authors')
.where({
id: article.author,
})
.fetch()
.catch(() => {
error({ statusCode: 404, message: 'Page not found' })
})
}
return { article, allArticles, author }
},
...
}
nuxt.config.js
import coinList from './data/coin_list.json'
export default {
target: 'static',
head: {
title: 'XXXXXX',
htmlAttrs: {
lang: 'de',
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' },
{ name: 'format-detection', content: 'telephone=no' },
{
hid: 'twitter:card',
property: 'twitter:card',
content: 'summary_large_image',
},
],
link: [
{
hid: 'apple-touch-icon',
rel: 'apple-touch-icon',
sizes: '180x180',
href: '/favicon.ico',
},
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
],
},
// Global CSS: https://go.nuxtjs.dev/config-css
css: [
'~/assets/css/tailwind.css',
'node_modules/lite-youtube-embed/src/lite-yt-embed.css',
],
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [
'~/plugins/metadata',
'~/plugins/youtube.client.js',
'~/plugins/jsonLd.js',
{ src: '@/plugins/vClickOutside', ssr: false },
'~/plugins/disqus',
],
// Auto import components: https://go.nuxtjs.dev/config-components
components: [
'~/components',
{ path: '~/components/utils', extensions: ['vue'] },
{ path: '~/components/global' },
],
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
buildModules: [
// https://go.nuxtjs.dev/tailwindcss
'@nuxtjs/tailwindcss',
[
'@nuxt/image',
{
provider: 'static',
},
],
'@/modules/sitemapRouteGenerator.js',
],
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
'@nuxtjs/axios',
'@nuxt/content',
'@nuxtjs/proxy',
'@nuxtjs/gtm',
// has to be last
'@nuxtjs/sitemap',
],
sitemap: {
hostname: process.env.NUXT_ENV_BASE_URL,
path: '/sitemap.xml',
},
// GTM Analytics
gtm: {
enabled: true,
pageTracking: true,
},
axios: {},
image: {
// Options
domains: ['assets.coingecko.com', 'coingecko.com'],
presets: {
blog: {
modifiers: {
format: 'webp',
},
},
},
},
// Content module configuration: https://go.nuxtjs.dev/config-content
content: {
// nestedProperties: ['articles.slug'],
},
generate: {
fallback: true,
async routes() {
const routes = await _getRoutes()
async function _getRoutes($content) {
const paths = []
coinList.forEach((coin) => {
paths.push({
route: `/coins/${coin.id}/historisch/`,
payload: coin.id,
})
})
return paths
}
return routes
},
},
router: {
routeNameSplitter: '/',
},
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
extend(config) {
config.module.rules.push({
test: /\.md$/,
loader: 'frontmatter-markdown-loader',
})
},
},
}
Module: sitemapRouteGenerator.js
export default function () {
this.nuxt.hook('generate:done', (context) => {
const routesToExclude = /\/index|\/articles\/|\/undefined/ // Add any route you don't want in your sitemap. Potentially get this from an .env file.
const allRoutes = Array.from(context.generatedRoutes)
// console.log(context.generatedRoutes)
const routes = allRoutes.filter((route) => !routesToExclude.test(route))
// console.log(routes)
this.nuxt.options.sitemap.routes = [...routes]
})
}