question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Generated blog posts are: "Duplicate, submitted URL not selected as canonical" according to Google Search Console

See original GitHub issue

Version

@nuxt/content: v1.10.0 nuxt: v2.14.4

Reproduction Link

Steps to reproduce

Not really sure there’s any steps to reproduce other than ensuring that a Nuxt JS website is in universal mode and is a static site.

Needless to say, here’s my nuxt.config.js file:

export default {
  /*
  ** Nuxt rendering mode
  ** See https://nuxtjs.org/api/configuration-mode
  */
  mode: 'universal',
  /*
  ** Nuxt target
  ** See https://nuxtjs.org/api/configuration-target
  */
  target: 'static',
  /*
  ** Env variables
  */
  env: {
    BASE_URL: process.env.BASE_URL || "https://domain-monitor.io",
    API_URL: process.env.API_URL || "http://127.0.0.1:8000",
    ONESIGNAL_PUSH_APP_ID: process.env.ONESIGNAL_PUSH_APP_ID || "",
    ONESIGNAL_SAFARI_WEB_ID: process.env.ONESIGNAL_SAFARI_WEB_ID || "",
    GA_ID: process.env.GA_ID || ""
  },
  /*
  ** Headers of the page
  ** See https://nuxtjs.org/api/configuration-head
  */
  head: {
    title: 'Domain Monitor',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'og:title', name: 'og:title', content: 'Domain Monitor' },
      { hid: 'description', name: 'description', content: 'Keep track of your expiring domains today for FREE with our FREE domain monitoring product.' },
      { hid: 'og:description', name: 'og:description', content: 'Keep track of your expiring domains today for FREE with our FREE domain monitoring product.' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
      { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap' },
      { rel: 'stylesheet', href: 'https://fonts.googleapis.com/icon?family=Material+Icons' }
    ]
  },
  /*
  ** Global CSS
  */
  css: [
    '@/assets/scss/domain-monitor.scss'
  ],
  /*
  ** Bootstrap Vue
  */
  bootstrapVue: {
    bootstrapCSS: false,
    bootstrapVueCSS: false
  },
  /*
  ** Plugins to load before mounting the App
  ** https://nuxtjs.org/guide/plugins
  */
  plugins: [
    { mode: 'client', src: '@/plugins/bootstrap-vue-icons' },
    { mode: 'client', src: '@/plugins/vue-axios' },
    { mode: 'client', src: '@/plugins/vee-validate' },
    { mode: 'client', src: '@/plugins/vue-moment' },
    { mode: 'client', src: '@/plugins/content-images' },
    { mode: 'client', src: '@/plugins/content-videos' }
  ],
  /*
  ** Auto import components
  ** See https://nuxtjs.org/api/configuration-components
  */
  components: true,
  /*
  ** Nuxt.js dev-modules
  */
  buildModules: [
    ['@nuxtjs/google-analytics', {
      id: process.env.GA_ID
    }]
  ],
  /*
  ** Nuxt.js modules
  */
  modules: [
    'bootstrap-vue/nuxt',
    '@nuxtjs/axios',
    '@nuxtjs/auth',
    '@nuxtjs/onesignal',
    '@nuxtjs/pwa',
    '@nuxt/content',
    '@nuxtjs/sitemap',
    ['@nuxtjs/component-cache', { maxAge: 1000 * 60 * 5 }] // 5 minutes
  ],
  /*
  ** Auth config
  */
  auth: {
    redirect: {
      login: '/account/login',
      logout: '/account/login',
      callback: '/account/login',
      home: '/account/domains'
    },
    strategies: {
      local: {
        login: { url: 'auth/login', method: 'post', propertyName: 'token' },
        logout: { url: 'account/logout', method: 'post' },
        user: { url: 'auth/user', method: 'get', propertyName: 'user' }
      }
    }
  },
  /*
  ** One Signal
  */
  oneSignal: {
    init: {
      appId: process.env.ONESIGNAL_PUSH_APP_ID,
      safari_web_id: process.env.ONESIGNAL_SAFARI_WEB_ID,
      allowLocalhostAsSecureOrigin: true,
      welcomeNotification: {
          disable: true
      }
    }
  },
  /*
  ** Axios module configuration
  ** See https://axios.nuxtjs.org/options
  */
  axios: {
    baseURL: process.env.API_URL
  },
  /*
  ** Build configuration
  ** See https://nuxtjs.org/api/configuration-build/
  */
  build: {
    extractCSS: true,

    extend (config, ctx) {
      const vueLoader = config.module.rules.find((rule) => rule.loader === 'vue-loader')
      vueLoader.options.transformAssetUrls = {
        video: ['src', 'poster'],
        source: 'src',
        img: 'src',
        image: 'xlink:href',
        'b-img': 'src',
        'b-img-lazy': ['src', 'blank-src'],
        'b-card': 'img-src',
        'b-card-img': 'img-src',
        'b-card-img-lazy': ['src', 'blank-src'],
        'b-carousel-slide': 'img-src',
        'b-embed': 'src'
      }
    }
  },
  /*
  ** Sitemap configuration
  ** See https://www.npmjs.com/package/@nuxtjs/sitemap#setup
  */
  sitemap: {
    hostname: process.env.BASE_URL, // https://www.yoursite.com
    exclude: [
      '/account/recovery',
      '/account/reset',
      '/account/profile',
      '/account/domains/add',
      '/account/domains',
      '/account/monitors/add',
      '/account/monitors',
      '/help/account/introduction',
      '/help/monitors/introduction',
      '/help/domains/introduction'
    ],
    routes() {
      return getRoutes();
    },
  }
}

I’ll also attach the template responsible for building up a blog post page (that I’ve generated) inside of pages/blog/_slug.vue

<template>
  <div>

    <section class="py-3 bg-light position-relative">
      <b-container>
        <b-row>
          <b-col cols="12" lg="8">
            <b-card no-body class="card--post">
              <div class="bg-primary card-img-top" v-if="post.image">
                <b-card-img :src="require(`~/assets/images/${post.image ? post.image : 'content/blog/default.svg'}`)" :alt="post.alt ? post.alt : 'Another Domain Monitor blog post just for you!'" top></b-card-img>
              </div>
              <b-card-body class="p-3 p-md-4">
                <b-row v-if="post.categories">
                  <b-col>
                    <h6 class="mb-n1">
                      <b-badge variant="light" v-for="(category, index) in post.categories" :key="index" class="text-lowercase mb-1 mr-1 p-1 p-md-2"># {{ category }}</b-badge>
                    </h6>
                  </b-col>
                </b-row>
                <b-row>
                  <b-col>
                    <nuxt-content :document="post" class="content content--post" />
                  </b-col>
                </b-row>
              </b-card-body>
            </b-card>
          </b-col>
          <b-col cols="12" lg="4">
            <ContentSearch class="mt-3 mt-lg-0" />
            <b-card no-body v-if="blogs" class="mt-3">
              <b-card-header class="bg-white">
                <b-row>
                  <b-col>
                    <h3 class="h5 font-weight-bold mb-0">More posts</h3>
                  </b-col>
                </b-row>
              </b-card-header>
              <b-list-group flush>
                <b-list-group-item v-for="(blog, index) in blogs" :key="index" :to="`/blog/${blog.slug}`" class="py-2 px-3">
                  <b-row>
                    <b-col cols="8">
                      <h6 class="mb-0 font-weight-bolder text-dark">{{ blog.title }}</h6>
                    </b-col>
                    <b-col class="d-flex align-items-center text-right">
                      <div class="w-100">
                        <b-button pill size="sm" type="button" variant="light" class="btn-action text-center">
                          <span class="material-icons md-18 d-flex align-items-center justify-content-around">arrow_forward</span>
                        </b-button>
                      </div>
                    </b-col>
                  </b-row>
                </b-list-group-item>
              </b-list-group>
            </b-card>
            <b-card no-body class="mt-3">
              <b-card-header class="bg-white">
                <b-row>
                  <b-col>
                    <h3 class="h5 font-weight-bold mb-0">All posts</h3>
                  </b-col>
                </b-row>
              </b-card-header>
              <b-list-group flush>
                <b-list-group-item to="/blog" class="py-2 px-3">
                  <b-row>
                    <b-col cols="8">
                      <h6 class="mb-0 font-weight-bolder text-dark">Read more of our great product news and updates</h6>
                    </b-col>
                    <b-col class="d-flex align-items-center text-right">
                      <div class="w-100">
                        <b-button pill size="sm" type="button" variant="light" class="btn-action text-center">
                          <span class="material-icons md-18 d-flex align-items-center justify-content-around">arrow_forward</span>
                        </b-button>
                      </div>
                    </b-col>
                  </b-row>
                </b-list-group-item>
              </b-list-group>
            </b-card>
          </b-col>
        </b-row>
      </b-container>
    </section>

  </div>
</template>

<script>
export default {
  layout: 'blog',
  head () {
    return {
      title: `${this.post.title} - Domain Monitor Blog`,
      meta: [
        { hid: 'og:title', name: 'og:title', content: `${this.post.title} - Domain Monitor Blog` },
        { hid: 'description', name: 'description', content: this.post.description },
        { hid: 'og:description', name: 'og:description', content: this.post.description }
      ]
    }
  },
  async asyncData ({ $content, params }) {
    const post = await $content('blog', params.slug)
                        .fetch()

    const blogs = await $content('blog')
                         .sortBy('createdAt', 'desc')
                         .limit(5)
                         .fetch()

    const [prev, next] = await $content('blog')
      .only(['title', 'slug'])
      .sortBy('createdAt', 'desc')
      .surround(params.slug)
      .fetch()

    return {
      post,
      prev,
      next,
      blogs
    }
  }
}
</script>

What is Expected?

I would’ve thought that when building a Nuxt JS website using npm run build && npm run generate that there wouldn’t be any duplicated URLs (supposedly indicated by search console).

What is actually happening?

Recently, I’ve been publishing content on a daily basis to my Nuxt JS website. I have content written within a /content/blog/ directory which is then built into pages when running the npm run build && npm run generate command. They’re also then visible within the sitemap.xml file.

I run these commands upon every deployment (after a new piece of content has been added) and then clear the cache in CloudFlare.

It’s been a few weeks now, and some of the content has been indexed just fine by Google, but for some reason, the vast majority of my blog post pages are “excluded” from search console because of:

Duplicate, submitted URL not selected as canonical

(see attached screenshot)

Now, I’m not sure whether this is an issue related to:

  • My configuration (although I doubt it because some pages have been indexed just fine)
  • The Content Module, since that’s where all of my content is written and I’m sure the Content Module plays some role in generating static files and has some code built in terms of what’s injected into each page.
  • Nuxt JS itself, but again, I don’t think so because my other pages (which aren’t blog related are just fine)
  • The Sitemap Module, but I wouldn’t want to duplicate this Github issue twice for something that could be more likely related to the Content Module

In terms of suggestions, supposedly there are two ways to resolve this from my research, both of which don’t seem right…

  1. Set a noindex meta tag on the pages which are showing this exclusion notice.
  2. Remove the page and redirect it to a similar page

Now, I’m trying to figure out more about why I might be getting this error, and if there’s some config or something I’ve missed, and what might be causing my exclusion errors since I can’t find anything in Search Console suggesting the root cause…

Screenshot 2020-12-07 at 14 19 31

My primary concern here is that the number of pages (all of my content pages) continue to rise and have a negative effect on my SEO.

Screenshot 2020-12-07 at 15 41 16

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:14 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
sts-ryan-holtoncommented, Dec 18, 2020

@davydnorris That’s interesting, yet I thought that there might be something up. After implementing what I said I would, I’ve actually seen that Google has brought back these pages, and so they’re not excluded any more!

Might be worth reacting to this comment for anyone else experience a similar issue, closing out this issue now then! 😃

Screenshot 2020-12-18 at 10 59 40
1reaction
Atinuxcommented, Dec 18, 2020

Thank you for your nice feedback @sts-ryan-holton

Read more comments on GitHub >

github_iconTop Results From Across the Web

Duplicate, submitted URL not selected as canonical
Go to Coverage > Duplicate, submitted URL not selected as canonical and use the URL Inspection tool to find out which URL was...
Read more >
What is the solution for "Submitted URL not selected as ...
Submitted URL not selected as canonical: The URL is one of a set of duplicate URLs without an explicitly marked canonical page. You...
Read more >
URL Canonicalization and the Canonical Tag | Documentation
When a site has duplicate content, Google chooses the canonical URL. Learn more about canonical URLs and how to consolidate duplicate URLs.
Read more >
How to Fix 'Duplicate, Submitted URL Not Selected as ...
Luckily, the 'Duplicate, Submitted URL Not Selected as Canonical' status is easy to find. You can use either the Google search console or...
Read more >
How to fix the "Duplicate, submitted URL not selected as ...
I have inspected the URL in Search Console and it says "Duplicate, submitted URL not selected as canonical" with Google picking up the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found