这是用户在 2024-4-20 23:15 为 https://blog.maximeheckel.com/posts/seo-mistakes-i-have-made-and-how-i-fixed-them/ 保存的双语快照页面,由 沉浸式翻译 提供双语支持。了解如何保存?
@MaximeHeckel

SEO mistakes I've made and how I fixed them
我犯过的 SEO 错误以及如何修复它们

October 13, 2020 / 15 min read
2020 年 10 月 13 日 /阅读 15 分钟

Last Updated: June 14, 2021
最后更新时间:2021 年 6 月 14 日

From 0 to 90k impressions in about a year, following Search Engine Optimization good practices was key to help to grow my blog and my audience. However, when I started it, I made terrible mistakes that some SEO literate people could almost qualify as self-sabotage.
大约一年内,展示次数从 0 增加到 9 万,遵循搜索引擎优化良好实践是帮助扩大我的博客和受众的关键。然而,当我开始时,我犯了一些可怕的错误,一些有 SEO 知识的人几乎可以称之为自我破坏。

Thus, I want to dedicate this blog post to look back at 3 issues that caused me, and many others, countless headaches when dealing with SEO and Gatsby and the steps I took to fix them. I hope that this will help to fix some issues you might currently have on your awesome blog or portfolio without even being aware of them, kick-off your audience growth, and get discovered online 🚀.
因此,我想在这篇博文中回顾一下在处理 SEO 和 Gatsby 时给我和其他许多人带来无数头痛的 3 个问题,以及我为解决这些问题所采取的步骤。我希望这将有助于解决您目前在精彩博客或作品集上可能存在的一些问题,甚至您自己都没有意识到,从而启动您的受众增长,并在网上被发现🚀。

Why SEO is so important?
为什么SEO如此重要?

You might know very little about what SEO does behind the scenes. To me, at least, it looked like an obscure, inconsistent, pseudo-science that only marketing people could understand (spoiler alert, it still kind of is). However, after getting @monicalent's awesome course bloggingfordevs, it made the inner workings and good practices related to SEO a bit clearer to me. To quote her from her first newsletter
您可能对 SEO 幕后的作用知之甚少。至少对我来说,它看起来像是一门晦涩难懂、不一致的伪科学,只有营销人员才能理解(剧透警告,它仍然是这样)。然而,在学习了 @monicalent 的精彩课程 bloggingfordevs 之后,它让我对 SEO 相关的内部运作和良好实践有了更清晰的了解。引用她的第一份时事通讯

SEO is a way of making sure that search engines can understand what your page is about, that it contains quality up-to-date information from an authoritative source, and will answer the question that the searcher had in mind.
SEO 是一种确保搜索引擎能够理解您的页面的内容、包含来自权威来源的优质最新信息并回答搜索者想到的问题的方法。

With good SEO, search engines can know what your content is about, discover all the blog posts you've written and, if you're lucky, catapult you to the top search result for a given set of keywords. Moreover, where sharing my newest articles on Twitter and Reddit would just cause a spike in traffic for a few days, SEO helps you get a more consistent traffic on your website, and for a longer time. The latter is what I was lacking for the longest time, despite having set up my Gatsby website and SEO component properly (or at least I thought so).
通过良好的搜索引擎优化,搜索引擎可以知道您的内容是关于什么的,发现您写的所有博客文章,如果幸运的话,可以将您推到给定关键字集的最佳搜索结果中。此外,在 Twitter 和 Reddit 上分享我的最新文章只会导致几天内的流量激增,而 SEO 可以帮助您在网站上获得更稳定的流量,并且持续时间更长。尽管我已经正确设置了我的 Gatsby 网站和 SEO 组件(或者至少我是这么认为的),但后者是我长期以来所缺乏的。

Gatsby's documentation has an incredibly well-written section on how to build an SEO component to help you get started. However, that alone wasn't enough to make my blog discoverable early on, as you can see in the chart below representing the number of daily impressions I got since starting this blog:
Gatsby 的文档中有一个写得非常好的部分,介绍如何构建 SEO 组件来帮助您入门。然而,仅凭这一点还不足以让我的博客尽早被发现,正如您在下面的图表中看到的那样,该图表代表了我自开始此博客以来获得的每日印象数:

Chart representing the number of impressions per day of this blog on Google Search from August 2019 to the October 2020 (hover to see the data)
该图表表示 2019 年 8 月至 2020 年 10 月期间此博客在 Google 搜索上的每日展示次数(将鼠标悬停即可查看数据)



For most of its first year, my blog was getting less than 50 daily impressions. Today, after fixing the issues I'm about to talk about, I get over 1000 daily impressions and it's still growing! Of course, SEO is not the only component here, I also created more content this year and choose a better way to promote them, but it is still a significant driver to the growth you can see above.
在第一年的大部分时间里,我的博客每天的浏览量不到 50 次。今天,在解决了我要讨论的问题后,我每天获得的印象数超过 1000,而且还在不断增长!当然,搜索引擎优化并不是这里唯一的组成部分,我今年还创建了更多内容并选择了更好的方式来推广它们,但它仍然是您在上面看到的增长的重要驱动力。

Trailing slashes chaos 尾随斜线混乱

The blog you're reading this article on is built with Gatsby and hosted on Netlify. Sadly, using these two tools together without taking care of inconsistent trailing slash / at the end of your URLs can result in some undesirable outcomes.
您正在阅读本文的博客是使用 Gatsby 构建并托管在 Netlify 上。遗憾的是,同时使用这两个工具而不注意 URL 末尾不一致的尾部斜杠 / 可能会导致一些不良结果。

One of these outcomes was that I was seeing a lot of 301 redirects logged in my analytics as readers were navigating to my articles. On my blog, a link to one of my blog posts would typically look like this: /posts/learning-in-public but when a reader clicked on it Netlify would append a trailing slash at the end of it thus redirecting the user.
结果之一是,当读者导航到我的文章时,我在分析中看到大量 301 重定向。在我的博客上,我的一篇博客文章的链接通常如下所示: /posts/learning-in-public ,但当读者单击它时,Netlify 会在其末尾添加一个尾部斜杠,从而重定向用户。

That, my friends, is extremely bad for SEO. It impacted several unrelated areas of my website, such as:
我的朋友们,这对于 SEO 来说是极其不利的。它影响了我网站的几个不相关的区域,例如:

  • ArrowAn icon representing an arrow
    Opengraph images or Twitter cards not being rendered consistently: readers would share a link sometimes with or without the trailing slash which would make it hard for some services to get the proper metadata and thus render a simple link instead of a proper preview card.
    Opengraph 图像或 Twitter 卡渲染不一致:读者有时会共享带或不带尾部斜杠的链接,这将使某些服务难以获取正确的元数据,从而呈现简单的链接而不是正确的预览卡。
  • ArrowAn icon representing an arrow
    Invalid URLs in sitemap: my sitemap is generated automatically at build time with a Gatsby plugin based on the URLs and pages of my website. Since I did not have trailing slashes at the end of my URLs it would generate my sitemap without them which once uploaded to Google Search Console would result in tons of warnings about invalid URLs since Google referenced the ones with the trailing slashes.
    站点地图中的 URL 无效:我的站点地图是在构建时使用 Gatsby 插件根据我网站的 URL 和页面自动生成的。由于我的网址末尾没有尾部斜杠,因此它会生成没有它们的站点地图,一旦上传到 Google Search Console,就会导致大量有关无效网址的警告,因为 Google 引用了带有尾部斜杠的网址。

How I fixed this
我是如何解决这个问题的

I could have fixed this in two different ways:
我可以用两种不同的方式解决这个问题:

  1. ArrowAn icon representing an arrow
    Disable the "Pretty URLs" option in Netlify's asset optimization settings. (see screenshot below)
    禁用 Netlify 资产优化设置中的“漂亮 URL”选项。 (见下面的截图)
  2. ArrowAn icon representing an arrow
    Add a trailing slash to all my URLs on my blog.
    在我博客上的所有 URL 中添加尾部斜杠。
Image showcasing the asset optimizations options available in the Netlify project settings. Here you can see that I have the pretty URL option turned on which will add a trailing slash at the end of my URLs on this project.
该图展示了 Netlify 项目设置中可用的资产优化选项。在这里您可以看到我打开了漂亮的 URL 选项,它将在该项目的 URL 末尾添加一个尾部斜杠。

As Google already referenced my blog posts with a trailing slash, I decided to go with option number 2.
由于 Google 已经引用了我的博客文章,并在后面添加了斜杠,因此我决定采用选项 2。

That change might look insignificant, but it resulted in a lot of weird issues suddenly disappearing. Additionally, it was essential for me to fix this before addressing the issue I'm just about to start talking about 😄!
这个变化可能看起来微不足道,但它导致很多奇怪的问题突然消失。此外,在解决我即将开始谈论的问题之前,我必须解决这个问题!

Server-side rendering and missing meta tags
服务器端渲染和缺少元标记

In this part, we'll look at the one instance where Gatsby's server-side rendering mixed with my carelessness completely broke my SEO. By completely I mean all my custom SEO meta tags that I carefully put in my SEO component were gone from the server-side rendered version of the website making it almost invisible to any search engine.
在这一部分中,我们将看看 Gatsby 的服务器端渲染与我的粗心混合完全破坏了我的 SEO 的一个实例。我所说的完全是指我小心翼翼地放入 SEO 组件中的所有自定义 SEO 元标记都已从网站的服务器端渲染版本中消失,使其对任何搜索引擎几乎不可见。

How it happened 这是怎么发生的

This issue stemmed from what I would qualify as an interrupted static HTML build.
这个问题源于我认为是中断的静态 HTML 构建。

When building your Gatsby site the last steps of the build process involve building your production JS files and also generating the HTML for each page. If you're looking for more details you can check out this section of the Gatsby documentation about the build process.
构建 Gatsby 站点时,构建过程的最后一步涉及构建生产 JS 文件以及为每个页面生成 HTML。如果您正在寻找更多详细信息,可以查看 Gatsby 文档中有关构建过程的这一部分。

However, I wrote a ThemeProvider that wrapped the whole application. Thus any component or page can know which theme (dark or light) is currently enabled and the colors to use. This component was added to the gatsby-ssr and gatsby-browser files.
然而,我写了一个 ThemeProvider 来包装整个应用程序。因此,任何组件或页面都可以知道当前启用了哪个主题(深色或浅色)以及要使用的颜色。该组件已添加到 gatsby-ssrgatsby-browser 文件中。

Under the hood, this ThemeProvider worked as follow:
在幕后,这个 ThemeProvider 的工作原理如下:

  • ArrowAn icon representing an arrow
    the state of the theme (dark or light) was injected via a React Provider to the whole app, that's how I can allow users to toggle between each theme.
    主题的状态(深色或浅色)通过 React Provider 注入到整个应用程序,这就是我允许用户在每个主题之间切换的方式。
  • ArrowAn icon representing an arrow
    that same state was also saved in the local storage to make sure revisiting the website would keep the previous theme enabled. When a reader loads this blog, the ThemeProvider will check for the presence of a specific variable in localStorage before setting the theme accordingly.
    相同的状态也保存在本地存储中,以确保重新访问网站时会保持先前的主题处于启用状态。当读者加载此博客时,ThemeProvider 将检查 localStorage 中是否存在特定变量,然后再相应地设置主题。

I dedicated a blog post for this: Switching off the lights - Adding dark mode to your React app and it actually contains the mistake that triggered the missing meta tags:
我为此专门写了一篇博文:关掉灯 - 向你的 React 应用程序添加黑暗模式,它实际上包含触发丢失元标记的错误:

  • ArrowAn icon representing an arrow
    Getting the variable set to the current theme from local storage was done in a React useEffect. Thus, for a brief instant when loading or refreshing the website, the website would fallback to the default theme as the effect to set the proper theme was only ran after the server-rendered page was already served.
    从本地存储中获取设置为当前主题的变量是在 React useEffect 中完成的。因此,在加载或刷新网站时的短暂瞬间,网站将回退到默认主题,因为设置正确主题的效果仅在服务器呈现的页面已经提供后运行。
  • ArrowAn icon representing an arrow
    To avoid this issue, I added a small tweak to track whether the theme was fetched from local storage or not and render an empty div while the theme was being retrieved.
    为了避免这个问题,我添加了一个小调整来跟踪主题是否从本地存储中获取,并在检索主题时渲染一个空的 div。

The code snippet below is an excerpt of my original implementation for the ThemeProvider of this blog.
下面的代码片段是我对本博客的 ThemeProvider 的原始实现的摘录。

Excerpt of my original ThemeProvider (where I made my dumb mistake)
我原来的 ThemeProvider 的摘录(我犯了愚蠢的错误)

1
const ThemeProvider = ({ children }: { children: ReactNode }) => {
2
const [themeState, setThemeState] = useDarkMode();
3
if (!themeState.hasThemeLoaded) {
4
/*
5
If the theme is not yet loaded we don't want to render
6
this is just a workaround to avoid having the app rendering
7
in light mode by default and then switch to dark mode while
8
getting the theme state from localStorage
9
*/
10
return <div />;
11
}
12
const theme = themeState.dark ? theme('dark') : theme('light');
13
const toggle = () => {
14
// toogle function goes here
15
};
16
17
// Once the theme is loaded, render the rest of the DOM
18
return (
19
<EmotionThemeProvider theme={theme}>
20
<ThemeContext.Provider
21
value={{
22
dark: themeState.dark,
23
toggle,
24
}}
25
>
26
{children}
27
</ThemeContext.Provider>
28
</EmotionThemeProvider>
29
);
30
};

Rendering that empty div is what made my SEO meta tags disappear. The static HTML build would only generate the tree up to that div since the theme had no way to be set at build time, and thus would skip all the rest of the DOM which included my pages and components, as well as the SEO component 😱. Since, the code of the SEO component was not being reached during that step of the build, the meta tags couldn't be injected in the static HTML.
渲染那个空 div 是让我的 SEO 元标签消失的原因。静态 HTML 构建只会生成到该 div 的树,因为主题无法在构建时设置,因此会跳过 DOM 的所有其余部分,其中包括我的页面和组件,以及 SEO 组件😱 。由于在构建的该步骤中未到达 SEO 组件的代码,因此无法将元标记注入静态 HTML 中。

Additionally, that kind of issue was hard to track because the tags were showing up when inspecting the page with the dev tools client-side, they were however completely absent from the source of the page (the one you can get by right-clicking on a web page and clicking on "View Page Source").
此外,此类问题很难跟踪,因为在使用开发工具客户端检查页面时会显示标签,但它们在页面源中完全不存在(您可以通过右键单击来获取该标签)网页并单击“查看页面源代码”)。

Having SEO tags completely missing from the page source made SEO third-party services like Twitter Card Validator simply unusable with my blog. My articles would only show up as basic links on social media. No cards, no preview, not even a title which is very bad when you try to grab the attention of your audience!
页面源中完全缺少 SEO 标签,导致 SEO 第三方服务(例如 Twitter Card Validator)根本无法在我的博客中使用。我的文章只会显示为社交媒体上的基本链接。没有卡片,没有预览,甚至没有标题,当您试图吸引观众的注意力时,这是非常糟糕的!

My SEO meta tags disappeared second time earlier this year in July 2020, after adding gatsby-plugin-feed and trying to make it work on my blog. Once again, be extremely careful when adding Gatsby plugins that can write on the <head/> of your pages. There might create some undesirable outcomes without even you knowing!
在添加 gatsby-plugin-feed 并尝试使其在我的博客上运行后,我的 SEO 元标记于今年早些时候(2020 年 7 月)第二次消失。再次强调,在添加可以在页面的 <head/> 上写入的 Gatsby 插件时要格外小心。可能会在您不知情的情况下产生一些不良后果!

The long term solution 长期解决方案

As you can imagine, I was tired of these issues coming out of nowhere and didn't want to manually check every single change I'd make in the future to ensure meta tags would not be removed. To this problem, I brought a solution that I'd usually bring up at work: I wrote an automated test.
正如您可以想象的那样,我厌倦了这些突然出现的问题,并且不想手动检查我将来所做的每一个更改以确保元标记不会被删除。对于这个问题,我提出了一个我在工作中通常会提出的解决方案:我编写了一个自动化测试。

My SEO tests that I run against every new build to ensure my SEO tags are intact
我针对每个新版本运行的 SEO 测试,以确保我的 SEO 标签完好无损

1
const META_RE = /<meta\s[A-Za-z0-9="-:;!@\/\s]*/g;
2
const CANONICAL_RE = /rel="canonical"\s[A-Za-z0-9="-:;!@\/\s]*/g;
3
4
describe('SEO: Verify meta tag integrity', () => {
5
it('has all the meta tags and the expected canonical url set in the landing page head', async () => {
6
const res = await fetch('/');
7
const text = await res.text();
8
9
const metaTags = text.match(META_RE) || [];
10
const canonicalTag = text.match(CANONICAL_RE) || [];
11
12
expect(metaTags).to.have.length(16);
13
expect(canonicalTag).to.have.length(1);
14
cy.wrap(metaTags).snapshot();
15
cy.wrap(canonicalTag).snapshot();
16
});
17
18
it('has all the meta tags and the expected canonical url set in the blog post head', async () => {
19
const res = await fetch('/posts/how-to-build-first-eslint-rule');
20
const text = await res.text();
21
22
const metaTags = text.match(META_RE) || [];
23
const canonicalTag = text.match(CANONICAL_RE) || [];
24
25
expect(metaTags).to.have.length(19);
26
expect(canonicalTag).to.have.length(1);
27
cy.wrap(metaTags).snapshot();
28
cy.wrap(canonicalTag).snapshot();
29
});
30
});

(Don't judge my regex skills 😅)
(不要评判我的正则表达式技能😅)

The code snippet above is the test that I run with Cypress on every PR without exception. This test:
上面的代码片段是我在每个 PR 上使用 Cypress 运行的测试,无一例外。本次测试:

  • ArrowAn icon representing an arrow
    fetches the source code of the landing page and a blog post against the built version of the blog
    根据博客的构建版本获取登陆页面的源代码和博客文章
  • ArrowAn icon representing an arrow
    looks at the text-based body of the request. That text-based result contains the HTML code for the entire page and thus should contain all the meta tags I set up in my SEO component.
    查看请求的基于文本的正文。该基于文本的结果包含整个页面的 HTML 代码,因此应该包含我在 SEO 组件中设置的所有元标记。
  • ArrowAn icon representing an arrow
    compares the obtained string of meta tags to a snapshot. That snapshot contains the source of truth when it comes to the expected state of my meta tags
    将获得的元标记字符串与快照进行比较。当涉及到我的元标记的预期状态时,该快照包含事实来源

Conclusion 结论

In a nutshell: 简而言之:

  • ArrowAn icon representing an arrow
    Be mindful of the consistency of your trailing slashes! Inconsistencies can lead to poor ranking.
    请注意尾部斜杠的一致性!不一致可能会导致排名不佳。
  • ArrowAn icon representing an arrow
    If you syndicate your content, do not forget to add canonical URLs. I was basically in competition with my own Medium posts up until late this year and missed on a lot more traffic and potential readers.
    如果您联合内容,请不要忘记添加规范 URL。直到今年年底,我基本上都在与自己的 Medium 帖子竞争,错过了更多的流量和潜在读者。
  • ArrowAn icon representing an arrow
    Do not blindly trust gatsby plugins! Especially the ones that inject things in the <head> of your pages. If misused, they can be pretty harmful without even you knowing.
    不要盲目相信 gatsby 插件!尤其是那些在页面的 <head> 中注入内容的。如果滥用,它们可能会在您不知情的情况下造成相当大的危害。
  • ArrowAn icon representing an arrow
    Check the page source of your website! Inspecting via the dev tools is sometimes not enough to ensure the meta tags are properly injected in your site.
    检查您网站的页面来源!通过开发工具进行检查有时不足以确保元标记正确注入您的网站。
  • ArrowAn icon representing an arrow
    Make sure your meta tags can't be blocked from rendering because of a client side side effect when relying on SSR.
    确保您的元标记不会因为依赖 SSR 时的客户端副作用而被阻止渲染。
  • ArrowAn icon representing an arrow
    When in doubt: Write tests! I dedicated a whole blog post about CI/CD where I showcase how great tests and a great CI/CD pipeline help me keep my peace of mind.
    如有疑问:编写测试!我专门写了一篇关于 CI/CD 的博客文章,其中展示了出色的测试和出色的 CI/CD 管道如何帮助我保持内心平静。

If you want to go further into how to build an audience, and learn more about creating content and SEO, I would highly encourage you to follow @monicalent on Twitter as well as her Blogging For Devs course. She's an SEO expert and I learned more about efficient SEO techniques in a single newsletter than I would have otherwise!
如果您想进一步了解如何建立受众群体,并了解有关创建内容和 SEO 的更多信息,我强烈建议您在 Twitter 上关注 @monicalent 以及她的 Blogging For Devs 课程。她是一位 SEO 专家,我在一份时事通讯中学到了比其他方式更多的关于高效 SEO 技术的知识!

Liked this article? Share it with a friend on Twitter or support me to take on more ambitious projects to write about. Have a question, feedback or simply wish to contact me privately? Shoot me a DM and I'll do my best to get back to you.
喜欢这篇文章吗?在 Twitter 上与朋友分享,或者支持我承担更雄心勃勃的项目来撰写。有疑问、反馈或只是想私下联系我?请给我发私信,我会尽力回复您。

Have a wonderful day. 祝你有美好的一天。

– Maxime ——马克西姆

A look back at what I learned fixing my terrible SEO mistakes on my Gatsby websites