{"id":1139,"date":"2020-06-15T14:34:34","date_gmt":"2020-06-15T14:34:34","guid":{"rendered":"https:\/\/lvboard.infostore.in.ua\/?p=1139"},"modified":"2020-06-15T14:34:35","modified_gmt":"2020-06-15T14:34:35","slug":"javascript-seo-what-you-need-to-know","status":"publish","type":"post","link":"https:\/\/lvboard.infostore.in.ua\/?p=1139","title":{"rendered":"JavaScript SEO: What You Need to Know"},"content":{"rendered":"\n<p>\n\nDid you know that while Ahrefs blog is powered by WordPress, much of the rest of the site is powered by JavaScript like React?<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>Most websites use some kind of JavaScript to add interactivity and to improve user experience. Some use it for menus, pulling in products or prices, grabbing content from multiple sources, or in some cases, for everything on the site. The reality of the current web is that JavaScript is ubiquitous.<\/p>\n\n\n\n<p>As Google\u2019s&nbsp;<a href=\"https:\/\/twitter.com\/JohnMu\" target=\"_blank\" rel=\"noreferrer noopener\">John Mueller<\/a>&nbsp;said:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/twitter.com\/JohnMu\"><img src=\"https:\/\/pbs.twimg.com\/profile_images\/1097426990699855873\/lEI3EWIL_normal.png\" alt=\"\"\/><\/a><\/figure>\n\n\n\n<p><a href=\"https:\/\/twitter.com\/JohnMu\"><strong>&nbsp;John&nbsp;<\/strong><strong>\u2714<\/strong>@JohnMu<\/a><a href=\"https:\/\/twitter.com\/JohnMu\/status\/894901485238190080\"><\/a><\/p>\n\n\n\n<p>The web has moved from plain HTML &#8211; as an SEO you can embrace that. Learn from JS devs &amp; share SEO knowledge with them. JS&#8217;s not going away.<a href=\"https:\/\/twitter.com\/intent\/like?tweet_id=894901485238190080\">247<\/a><a href=\"https:\/\/twitter.com\/JohnMu\/status\/894901485238190080\">3:41 PM &#8211; Aug 8, 2017<\/a><a href=\"https:\/\/support.twitter.com\/articles\/20175256\">Twitter Ads info and privacy<\/a><a href=\"https:\/\/twitter.com\/JohnMu\/status\/894901485238190080\">192 people are talking about this<\/a><\/p>\n\n\n\n<p>I\u2019m not saying that SEOs need to go out and learn how to program JavaScript. It\u2019s quite the opposite. SEOs mostly need to know how Google handles JavaScript and how to troubleshoot issues. In very few cases will an&nbsp;SEO&nbsp;even be allowed to touch the code. My goal with this post is to help you learn:<\/p>\n\n\n\n<ul><li><a href=\"https:\/\/ahrefs.com\/blog\/javascript-seo\/?fbclid=IwAR0dwUw3u9x0auodDZBgXX7Tf4VafcF59W1BsvQmR-EjjwBBM6vkszlXOmQ#what-is-javascript-seo\">What is Javascript SEO<\/a><\/li><li><a href=\"https:\/\/ahrefs.com\/blog\/javascript-seo\/?fbclid=IwAR0dwUw3u9x0auodDZBgXX7Tf4VafcF59W1BsvQmR-EjjwBBM6vkszlXOmQ#crawling-processing-rendering\">How Google processes pages with JavaScript<\/a><\/li><li><a href=\"https:\/\/ahrefs.com\/blog\/javascript-seo\/?fbclid=IwAR0dwUw3u9x0auodDZBgXX7Tf4VafcF59W1BsvQmR-EjjwBBM6vkszlXOmQ#testing-troubleshooting\">How to test and troubleshoot JavaScript<\/a><\/li><li><a href=\"https:\/\/ahrefs.com\/blog\/javascript-seo\/?fbclid=IwAR0dwUw3u9x0auodDZBgXX7Tf4VafcF59W1BsvQmR-EjjwBBM6vkszlXOmQ#render-options\">Rendering options<\/a><\/li><li><a href=\"https:\/\/ahrefs.com\/blog\/javascript-seo\/?fbclid=IwAR0dwUw3u9x0auodDZBgXX7Tf4VafcF59W1BsvQmR-EjjwBBM6vkszlXOmQ#making-javascript-site-seo-friendly\">Making your JavaScript site SEO friendly<\/a><\/li><\/ul>\n\n\n\n<p><a><\/a><\/p>\n\n\n\n<h2>What is&nbsp;JavaScript&nbsp;SEO?<\/h2>\n\n\n\n<p>JavaScript&nbsp;SEO&nbsp;is a part of&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/technical-seo\/\">Technical&nbsp;SEO<\/a>&nbsp;(Search Engine Optimization) that seeks to make JavaScript-heavy websites easy to crawl and index, as well as search-friendly. The goal is to have these websites be found and&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/how-to-rank-higher-on-google\/\">rank higher in search engines<\/a>.<\/p>\n\n\n\n<p>Is JavaScript bad for&nbsp;SEO; is JavaScript evil? Not at all. It\u2019s just different from what many SEOs are used to, and there\u2019s a bit of a learning curve. People do tend to overuse it for things where there\u2019s probably a better solution, but you have to work with what you have at times. Just know that Javascript isn\u2019t perfect and it isn\u2019t always the right tool for the job. It can\u2019t be parsed progressively, unlike&nbsp;HTML&nbsp;and&nbsp;CSS, and it can be heavy on page load and performance. In many cases, you may be trading performance for functionality.<a><\/a><\/p>\n\n\n\n<h2>How Google processes pages with JavaScript<\/h2>\n\n\n\n<p>In the early days of search engines, a downloaded&nbsp;HTML&nbsp;response was enough to see the content of most pages. Thanks to the rise of JavaScript, search engines now need to render many pages as a browser would so they can see content how a user sees it.<\/p>\n\n\n\n<p>The system that handles the rendering process at Google is called the Web Rendering Service (WRS). Google has provided a simplistic diagram to cover how this process works.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/ahrefs.com\/blog\/wp-content\/uploads\/2020\/06\/pasted-image-0.png\" alt=\"pasted image 0\" class=\"wp-image-36759\"\/><\/figure>\n\n\n\n<p><a href=\"https:\/\/developers.google.com\/search\/docs\/guides\/javascript-seo-basics\" target=\"_blank\" rel=\"noreferrer noopener\">Source<\/a><\/p>\n\n\n\n<p>Let\u2019s say we start the process at&nbsp;URL.<\/p>\n\n\n\n<h3>1. Crawler<\/h3>\n\n\n\n<p>The crawler sends&nbsp;GET&nbsp;requests to the server. The server responds with headers and the contents of the file, which then gets saved.<\/p>\n\n\n\n<p>The request is likely to come from a mobile user-agent since Google is mostly on&nbsp;<a href=\"https:\/\/webmasters.googleblog.com\/2016\/11\/mobile-first-indexing.html\" target=\"_blank\" rel=\"noreferrer noopener\">mobile-first indexing<\/a>&nbsp;now. You can check to see how Google is crawling your site with the&nbsp;<a href=\"https:\/\/support.google.com\/webmasters\/answer\/9012289?hl=en\" target=\"_blank\" rel=\"noreferrer noopener\">URL&nbsp;Inspection Tool<\/a>&nbsp;inside&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/google-search-console\/\">Search Console<\/a>. When you run this for a&nbsp;URL, check the Coverage information for \u201cCrawled as,\u201d and it should tell you whether you\u2019re still on desktop indexing or mobile-first indexing.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/ahrefs.com\/blog\/wp-content\/uploads\/2020\/06\/1.search-console-crawled-as.png\" alt=\"1.search console crawled as\" class=\"wp-image-36764\"\/><\/figure>\n\n\n\n<p>The requests mostly come from Mountain View,&nbsp;CA,&nbsp;USA, but they also do&nbsp;<a href=\"https:\/\/support.google.com\/webmasters\/answer\/6144055?hl=en\" target=\"_blank\" rel=\"noreferrer noopener\">some crawling for locale-adaptive pages<\/a>&nbsp;outside of the United States. I mention this because some sites will block or treat visitors from a specific country or using a particular&nbsp;IP&nbsp;in different ways, which could cause your content not to be seen by Googlebot.<\/p>\n\n\n\n<p>Some sites may also use user-agent detection to show content to a specific crawler. Especially with JavaScript sites, Google may be seeing something different than a user. This is why Google tools such as the&nbsp;URL&nbsp;Inspection Tool&nbsp;inside Google Search Console, the&nbsp;<a href=\"https:\/\/search.google.com\/test\/mobile-friendly\" target=\"_blank\" rel=\"noreferrer noopener\">Mobile-Friendly Test<\/a>, and the&nbsp;<a href=\"https:\/\/search.google.com\/test\/rich-results\" target=\"_blank\" rel=\"noreferrer noopener\">Rich Results Test<\/a>&nbsp;are important for troubleshooting JavaScript&nbsp;SEO&nbsp;issues. They show you what Google sees and are useful for checking if Google may be blocked and if they can see the content on the page. I\u2019ll cover how to test this in the section about the Renderer because there are some key differences between the downloaded&nbsp;GET&nbsp;request, the rendered page, and even the testing tools.<\/p>\n\n\n\n<p>It\u2019s also important to note that while Google states the output of the crawling process as \u201cHTML\u201d on the image above, in reality, they\u2019re crawling and storing all resources needed to build the page.&nbsp;HTML&nbsp;pages, Javascript files,&nbsp;CSS,&nbsp;XHR&nbsp;requests,&nbsp;API&nbsp;endpoints, and more.<\/p>\n\n\n\n<h3>2. Processing<\/h3>\n\n\n\n<p>There are a lot of systems obfuscated by the term \u201cProcessing\u201d in the image. I\u2019m going to cover a few of these that are relevant to JavaScript.<\/p>\n\n\n\n<h4>Resources and Links<\/h4>\n\n\n\n<p>Google does not navigate from page to page as a user would. Part of Processing is to check the page for links to other pages and files needed to build the page. These links are pulled out and added to the crawl queue, which is what Google is using to prioritize and schedule crawling.<\/p>\n\n\n\n<p>Google will pull resource links (CSS,&nbsp;JS, etc.) needed to build a page from things like&nbsp;<code>&lt;link&gt;<\/code>&nbsp;tags. However, links to other pages need to be in a specific format for Google to treat them as links. Internal and external links need to be an&nbsp;<code>&lt;a&gt;<\/code>&nbsp;tag with an&nbsp;<code>href<\/code>&nbsp;attribute. There are many ways you can make this work for users with JavaScript that are not search-friendly.<\/p>\n\n\n\n<p><strong>Good:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;a href=\u201d\/page\u201d&gt;simple is good&lt;\/a&gt;\n&lt;a href=\u201d\/page\u201d onclick=\u201dgoTo(\u2018page\u2019)\u201d&gt;still okay&lt;\/a&gt;\n<\/pre>\n\n\n\n<p><strong>Bad:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;a onclick=\u201dgoTo(\u2018page\u2019)\u201d&gt;nope, no href&lt;\/a&gt;\n&lt;a href=\u201djavascript:goTo(\u2018page\u2019)\u201d&gt;nope, missing link&lt;\/a&gt;\n&lt;a href=\u201djavascript:void(0)\u201d&gt;nope, missing link&lt;\/a&gt;\n&lt;span onclick=\u201dgoTo(\u2018page\u2019)\u201d&gt;not the right HTML element&lt;\/span&gt;\n&lt;option value=\"page\"&gt;nope, wrong HTML element&lt;\/option&gt;\n&lt;a href=\u201d#\u201d&gt;no link&lt;\/a&gt;\nButton, ng-click, there are many more ways this can be done incorrectly.\n<\/pre>\n\n\n\n<p>It\u2019s also worth noting that&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/internal-links-for-seo\/\">internal links<\/a>&nbsp;added with JavaScript will not get picked up until after rendering. That should be relatively quick and not a cause for concern in most cases.<\/p>\n\n\n\n<h4>Caching<\/h4>\n\n\n\n<p>Every file that Google downloads, including&nbsp;HTML&nbsp;pages, JavaScript files,&nbsp;CSS&nbsp;files, etc., is going to be aggressively cached. Google will ignore your cache timings and fetch a new copy when they want to. I\u2019ll talk a bit more about this and why it\u2019s important in the Renderer section.<\/p>\n\n\n\n<h4>Duplicate elimination<\/h4>\n\n\n\n<p><a href=\"https:\/\/ahrefs.com\/blog\/duplicate-content\/\">Duplicate content<\/a>&nbsp;may be eliminated or deprioritized from the downloaded&nbsp;HTML&nbsp;before it gets sent to rendering. With app shell models, very little content and code may be shown in the&nbsp;HTML&nbsp;response. In fact, every page on the site may display the same code, and this could be the same code shown on multiple websites. This can sometimes cause pages to be treated as duplicates and not immediately go to rendering. Even worse, the wrong page or even the wrong site may show in search results. This should resolve itself over time but can be problematic, especially with newer websites.<\/p>\n\n\n\n<h4>Most Restrictive Directives<\/h4>\n\n\n\n<p>Google will choose the most restrictive&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/meta-robots\/\">statements<\/a>&nbsp;between&nbsp;HTML&nbsp;and the rendered version of a page. If JavaScript changes a statement and that conflicts with the statement from&nbsp;HTML, Google will simply obey whichever is the most restrictive. Noindex will override index, and noindex in&nbsp;HTML&nbsp;will skip rendering altogether.<\/p>\n\n\n\n<h3>3. Render queue<\/h3>\n\n\n\n<p>Every page goes to the renderer now. One of the biggest concerns from many SEOs with JavaScript and two-stage indexing (HTML&nbsp;then rendered page) is that pages might not get rendered for days or even weeks. When Google looked into this, they found&nbsp;<a href=\"https:\/\/twitter.com\/igrigorik\/status\/1194427659867967490\" target=\"_blank\" rel=\"noreferrer noopener\">pages went to the renderer at a median time of 5 seconds<\/a>, and the 90th percentile was minutes. So the amount of time between getting the&nbsp;HTML&nbsp;and rendering the pages should not be a concern in most cases.<\/p>\n\n\n\n<h3>4. Renderer<\/h3>\n\n\n\n<p>The renderer is where Google renders a page to see what a user sees. This is where they\u2019re going to process the JavaScript and any changes made by JavaScript to the&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Document_Object_Model\" target=\"_blank\" rel=\"noreferrer noopener\">Document Object Model (DOM)<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/ahrefs.com\/blog\/wp-content\/uploads\/2020\/06\/01-javascript-seo.png\" alt=\"01 javascript seo\" class=\"wp-image-36760\"\/><\/figure>\n\n\n\n<p>For this, Google is using a headless Chrome browser that is now \u201cevergreen,\u201d which means it should use the latest Chrome version and support the latest features. Until recently, Google was rendering with Chrome 41, so many features were not supported.<\/p>\n\n\n\n<p>Google has more&nbsp;<a href=\"https:\/\/developers.google.com\/search\/docs\/guides\/fix-search-javascript\" target=\"_blank\" rel=\"noreferrer noopener\">info on the Web Rendering Service (WRS)<\/a>,&nbsp;which includes things like denying permissions, being stateless, flattening light&nbsp;DOM&nbsp;and shadow&nbsp;DOM, and more that is worth reading.<\/p>\n\n\n\n<p>Rendering at web-scale may be the 8th wonder of the world. It\u2019s a serious undertaking and takes a tremendous amount of resources. Because of the scale, Google is taking many shortcuts with the rendering process to speed things up. At Ahrefs, we are the only major&nbsp;SEO&nbsp;tool that&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/crawling-javascript\/\">renders web pages at scale<\/a>, and we manage to render ~150M&nbsp;pages a day to make our link index more complete. It allows us to check for JavaScript redirects and we can also show links we found inserted with JavaScript which we show with a&nbsp;JS&nbsp;tag in the link reports:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/ahrefs.com\/blog\/wp-content\/uploads\/2020\/06\/3-js-site-explorer.png\" alt=\"3 js site explorer\" class=\"wp-image-36761\"\/><\/figure>\n\n\n\n<h4>Cached Resources<\/h4>\n\n\n\n<p>Google is relying heavily on caching resources. Pages are cached; files are cached;&nbsp;API&nbsp;requests are cached; basically, everything is cached before being sent to the renderer. They\u2019re not going out and downloading each resource for every page load, but instead using cached resources to speed up this process.<\/p>\n\n\n\n<p>This can lead to some impossible states where previous file versions are used in the rendering process and the indexed version of a page may contain parts of older files. You can use file versioning or content fingerprinting to generate new file names when significant changes are made so that Google has to download the updated version of the resource for rendering.<\/p>\n\n\n\n<h4>No Fixed Timeout<\/h4>\n\n\n\n<p>A common&nbsp;SEO&nbsp;myth is that the renderer only waits five seconds to load your page. While it\u2019s always a good idea to&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/advanced-pagespeed-guide\/\">make your site faster<\/a>, this myth doesn\u2019t really make sense with the way Google caches files mentioned above. They\u2019re basically loading a page with everything cached already. The myth comes from the testing tools like the&nbsp;URL&nbsp;Inspection Tool where resources are fetched live and they need to set a reasonable limit.<\/p>\n\n\n\n<p>There is no fixed timeout for the renderer. What they are likely doing is something similar to what the public&nbsp;<a href=\"https:\/\/github.com\/GoogleChrome\/rendertron\" target=\"_blank\" rel=\"noreferrer noopener\">Rendertron<\/a>&nbsp;does. They likely wait for something like networkidle0 where no more network activity is occurring and also set a maximum amount of time in case something gets stuck&nbsp;or someone is trying to mine bitcoin on their pages.<\/p>\n\n\n\n<h4>What Googlebot Sees<\/h4>\n\n\n\n<p>Googlebot doesn\u2019t take action on webpages. They\u2019re not going to click things or scroll, but that doesn\u2019t mean they don\u2019t have workarounds. For content, as long as it is loaded in the&nbsp;DOM&nbsp;without a needed action, they will see it. I will cover this more in the troubleshooting section but basically, if the content is in the&nbsp;DOM&nbsp;but just hidden, it will be seen. If it\u2019s not loaded into the&nbsp;DOM&nbsp;until after a click, then the content won\u2019t be found.<\/p>\n\n\n\n<p>Google doesn\u2019t need to scroll to see your content either because they have a clever workaround to see the content. For mobile, they load the page with a screen size of 411&#215;731 pixels and&nbsp;<a href=\"https:\/\/twitter.com\/jroakes\/status\/953604145327624192\" target=\"_blank\" rel=\"noreferrer noopener\">resize the length to 12,140 pixels<\/a>. Essentially, it becomes a really long phone with a screen size of 411&#215;12140 pixels. For desktop, they do the same and go from 1024&#215;768 pixels to 1024&#215;9307 pixels.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/ahrefs.com\/blog\/wp-content\/uploads\/2020\/06\/02-javascript-seo.png\" alt=\"02 javascript seo\" class=\"wp-image-36767\"\/><\/figure>\n\n\n\n<p>Another interesting shortcut is that Google doesn\u2019t paint the pixels during the rendering process. It takes time and additional resources to finish a page load, and they don\u2019t really need to see the final state with the pixels painted. They just need to know the structure and the layout and they get that without having to actually paint the pixels. As&nbsp;<a href=\"https:\/\/twitter.com\/g33konaut\" target=\"_blank\" rel=\"noreferrer noopener\">Martin Splitt<\/a>&nbsp;from Google puts it:<\/p>\n\n\n\n<figure><iframe src=\"https:\/\/www.youtube.com\/embed\/Qxd_d9m9vzo?autohide=1&amp;iv_load_policy=3&amp;modestbranding=1&amp;rel=0&amp;wmode=transparent&amp;autoplay=0\" allowfullscreen=\"\"><\/iframe><\/figure>\n\n\n\n<blockquote class=\"wp-block-quote\"><p>In Google search we don\u2019t really care about the pixels because we don\u2019t really want to show it to someone. We want to process the information and the semantic information so we need something in the intermediate state. We don\u2019t have to actually paint the pixels.<\/p><\/blockquote>\n\n\n\n<p>A visual might help explain what is cut out a bit better. In Chrome Dev Tools, if you run a test on the Performance tab you get a loading chart. The solid green part here represents the painting stage and for Googlebot that never happens so they save resources.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/ahrefs.com\/blog\/wp-content\/uploads\/2020\/06\/chrome-dev-tools.png\" alt=\"chrome dev tools\" class=\"wp-image-36762\"\/><\/figure>\n\n\n\n<p><strong>Gray<\/strong>&nbsp;= downloads<br><strong>Blue<\/strong>&nbsp;=&nbsp;HTML<br><strong>Yellow<\/strong>&nbsp;= JavaScript<br><strong>Purple<\/strong>&nbsp;= Layout<br><strong>Green<\/strong>&nbsp;= Painting<\/p>\n\n\n\n<h3>5. Crawl queue<\/h3>\n\n\n\n<p>Google has a resource that talks a bit about&nbsp;<a href=\"https:\/\/webmasters.googleblog.com\/2017\/01\/what-crawl-budget-means-for-googlebot.html\" target=\"_blank\" rel=\"noreferrer noopener\">crawl budget<\/a>, but you should know that each site has its own crawl budget, and each request has to be prioritized. Google also has to balance your site crawling vs. every other site on the internet. Newer sites in general or sites with a lot of dynamic pages will likely be crawled slower. Some pages will be updated less often than others, and some resources may also be requested less frequently.<a><\/a><\/p>\n\n\n\n<h2>Testing \/ troubleshooting<\/h2>\n\n\n\n<p>One \u2018gotcha\u2019 with JavaScript sites is they can update only parts of the&nbsp;DOM. Browsing to another page as a user may not update some aspects like title tags or canonical tags in the&nbsp;DOM, but this may not be an issue for search engines. Remember, Google loads each page stateless, so they\u2019re not saving previous information and are not navigating between pages. I\u2019ve seen SEOs get tripped up thinking there is a problem because of what they see after navigating from one page to another, such as a canonical tag that doesn\u2019t update, but Google may never see this state. Devs can fix this by updating the state using what\u2019s called the&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/History_API\" target=\"_blank\" rel=\"noreferrer noopener\">History&nbsp;API<\/a>, but again it may not be a problem. Refresh the page and see what you see or better yet run it through one of Google\u2019s testing tools to see what they see. More on those in a second.<\/p>\n\n\n\n<h3>View-source vs. Inspect<\/h3>\n\n\n\n<p>When you right-click in a browser window, you\u2019ll see a couple of options for viewing the source code of the page and for inspecting the page. View-source is going to show you the same as a&nbsp;GET&nbsp;request would. This is the raw&nbsp;HTML&nbsp;of the page. Inspect shows you the processed&nbsp;DOM&nbsp;after changes have been made and is closer to the content that Googlebot sees. It\u2019s basically the updated and latest version of the page. You should use inspect over view-source when working with JavaScript.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/ahrefs.com\/blog\/wp-content\/uploads\/2020\/06\/2-view-source-inspect-element.png\" alt=\"2 view source inspect element\" class=\"wp-image-36763\"\/><\/figure>\n\n\n\n<h3>Google Cache<\/h3>\n\n\n\n<p>Google\u2019s cache is not a reliable way to check what Googlebot sees. It\u2019s usually the initial&nbsp;HTML, although it\u2019s sometimes the rendered&nbsp;HTML&nbsp;or an older version. The system was made to see the content when a website is down. It\u2019s not particularly useful as a debug tool.<\/p>\n\n\n\n<h3>Google Testing Tools<\/h3>\n\n\n\n<p>Google\u2019s testing tools like the&nbsp;URL&nbsp;Inspector inside Google Search Console, Mobile Friendly Tester, Rich Results Tester are useful for debugging. Still, even these tools are slightly different from what Google will see. I already talked about the five-second timeout in these tools that the renderer doesn\u2019t have, but these tools also differ in that they\u2019re pulling resources in real-time and not using the cached versions as the renderer would. The screenshots in these tools also show pages with the pixels painted, which Google doesn\u2019t see in the renderer.<\/p>\n\n\n\n<p>The tools are useful to see if content is DOM-loaded, though. The&nbsp;HTML&nbsp;shown in these tools is the rendered&nbsp;DOM. You can search for a snippet of text to see if it was loaded in by default.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/ahrefs.com\/blog\/wp-content\/uploads\/2020\/06\/4-mobile-friendly-tool.png\" alt=\"4 mobile friendly tool\" class=\"wp-image-36757\"\/><\/figure>\n\n\n\n<p>The tools will also show you resources that may be blocked and console error messages which are useful for debugging.<\/p>\n\n\n\n<h3>Searching Text in Google<\/h3>\n\n\n\n<p>Another quick check you can do is simply search for a snippet of your content in Google. Search for \u201csome phrase from your content\u201d and see if the page is returned. If it is, then your content was likely seen. Note that content that is hidden by default may not be surfaced within your snippet on the&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/serps\/\">SERPs<\/a>.<\/p>\n\n\n\n<h3>Ahrefs<\/h3>\n\n\n\n<p>Along with the link index rendering pages, you can enable JavaScript in&nbsp;<a href=\"https:\/\/ahrefs.com\/site-audit\">Site Audit<\/a>&nbsp;crawls to unlock more data in your audits.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/ahrefs.com\/blog\/wp-content\/uploads\/2020\/06\/5-site-audit-javascript.png\" alt=\"5 site audit javascript\" class=\"wp-image-36765\"\/><\/figure>\n\n\n\n<p>The&nbsp;<a href=\"https:\/\/ahrefs.com\/seo-toolbar\">Ahrefs Toolbar<\/a>&nbsp;also supports JavaScript and allows you to compare&nbsp;HTML&nbsp;to rendered versions of tags.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/ahrefs.com\/blog\/wp-content\/uploads\/2020\/06\/ahrefs-seo-toolbar-js.png\" alt=\"ahrefs seo toolbar js\" class=\"wp-image-36766\"\/><\/figure>\n\n\n\n<p><a><\/a><\/p>\n\n\n\n<h2>Render options<\/h2>\n\n\n\n<p>There are lots of options when it comes to rendering JavaScript. Google has a solid chart that I\u2019m just going to show. Any kind of&nbsp;SSR, static rendering, prerendering setup is going to be fine for search engines. The main one that causes problems is full client-side rendering where all of the rendering happens in the browser.<\/p>\n\n\n\n<p>While Google would probably be okay even with client-side rendering, it\u2019s best to choose a different rendering option to support other&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/alternative-search-engines\/\">search engines<\/a>. Bing also has&nbsp;<a href=\"https:\/\/blogs.bing.com\/webmaster\/october-2019\/The-new-evergreen-Bingbot-simplifying-SEO-by-leveraging-Microsoft-Edge\/\" target=\"_blank\" rel=\"noreferrer noopener\">support for JavaScript rendering<\/a>, but the scale is unknown. Yandex and Baidu have limited support from what I\u2019ve seen, and many other search engines have little to no support for JavaScript.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/ahrefs.com\/blog\/wp-content\/uploads\/2020\/06\/google-rendering-on-the-web-infographic.jpg\" alt=\"google rendering on the web infographic\" class=\"wp-image-36758\"\/><\/figure>\n\n\n\n<p><a href=\"https:\/\/developers.google.com\/web\/updates\/2019\/02\/rendering-on-the-web\" target=\"_blank\" rel=\"noreferrer noopener\">Source<\/a><\/p>\n\n\n\n<p>There\u2019s also the option of&nbsp;<a href=\"https:\/\/developers.google.com\/search\/docs\/guides\/dynamic-rendering\" target=\"_blank\" rel=\"noreferrer noopener\">Dynamic Rendering<\/a>,&nbsp;which is rendering for certain user-agents. This is basically a workaround but can be useful to render for certain bots like search engines or even social media bots. Social media bots don\u2019t run JavaScript, so things like&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/open-graph-meta-tags\/\">OG&nbsp;tags<\/a>&nbsp;won\u2019t be seen unless you render the content before serving it to them.<\/p>\n\n\n\n<p>If you were using the old&nbsp;<a href=\"https:\/\/developers.google.com\/webmasters\/ajax-crawling\/docs\/learn-more\" target=\"_blank\" rel=\"noreferrer noopener\">AJAX&nbsp;crawling scheme<\/a>, note that this has been deprecated and may no longer be supported.<a><\/a><\/p>\n\n\n\n<h2>Making your JavaScript site&nbsp;SEO&nbsp;friendly<\/h2>\n\n\n\n<p>A lot of the processes are similar to things SEOs are already used to seeing, but there might be slight differences.<\/p>\n\n\n\n<h3>On-page&nbsp;SEO<\/h3>\n\n\n\n<p>All the normal on-page&nbsp;SEO&nbsp;rules for content,&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/title-tag-seo\/\">title tags<\/a>,&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/meta-description-study\/\">meta descriptions<\/a>,&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/alt-text\/\">alt attributes<\/a>,&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/meta-robots\/\">meta robot tags<\/a>, etc. still apply. See&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/on-page-seo\/\">On-Page&nbsp;SEO: An Actionable Guide<\/a>.<\/p>\n\n\n\n<p>A couple of issues I repeatedly see when working with JavaScript websites are that titles and descriptions may be reused and that alt attributes on images are rarely set.<\/p>\n\n\n\n<h3>Allow crawling<\/h3>\n\n\n\n<p>Don\u2019t block access to resources. Google needs to be able to access and download resources so that they can render the pages properly. In your&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/robots-txt\/\">robots.txt<\/a>, the easiest way to allow the needed resources to be crawled is to add:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">User-Agent: Googlebot\nAllow: .js\nAllow: .css\n<\/pre>\n\n\n\n<h3>URLs<\/h3>\n\n\n\n<p>Change URLs when updating content. I already mentioned the History&nbsp;API, but you should know that with JavaScript frameworks, they\u2019re going to have a router that lets you map to&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/seo-friendly-urls\/\">clean URLs<\/a>. You don\u2019t want to use hashes (#) for routing. This is especially a problem for Vue and some of the earlier versions of Angular. So for a&nbsp;URL&nbsp;like abc.com\/#something, anything after a # is typically ignored by a server. To fix this for Vue, you can work with your developer to change the following:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Vue router: \nUse \u2018History\u2019 Mode instead of the traditional \u2018Hash\u2019 Mode.\n\nconst router = new VueRouter ({\nmode: \u2018history\u2019,\nrouter: [] \/\/the array of router links\n)}\n<\/pre>\n\n\n\n<h3>Duplicate content<\/h3>\n\n\n\n<p>With JavaScript, there may be several URLs for the same content, which leads to duplicate content issues. This may be caused by capitalization, IDs, parameters with IDs, etc. So, all of these may exist:<\/p>\n\n\n\n<p><code>domain.com\/Abc<\/code><br><code>domain.com\/abc<\/code><br><code>domain.com\/123<\/code><br><code>domain.com\/?id=123<\/code><\/p>\n\n\n\n<p>The solution is simple. Choose one version you want indexed and set&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/canonical-tags\/\">canonical tags<\/a>.<\/p>\n\n\n\n<h3>SEO&nbsp;\u201cplugin\u201d type options<\/h3>\n\n\n\n<p>For JavaScript frameworks, these are usually referred to as modules. You\u2019ll find versions for many of the popular frameworks like React, Vue, and Angular by searching for the framework + module name like \u201cReact Helmet.\u201d Meta tags, Helmet, and Head are all popular modules with similar functionality allowing you to set many of the popular tags needed for&nbsp;SEO.<\/p>\n\n\n\n<h3>Error pages<\/h3>\n\n\n\n<p>Because JavaScript frameworks aren\u2019t server-side, they can\u2019t really throw a server error like a 404. You have a couple of different options for error pages:<\/p>\n\n\n\n<ol><li>Use a JavaScript redirect to a page that does respond with a 404 status code<\/li><li>Add a noindex tag to the page that\u2019s failing along with some kind of error message like \u201c404 Page Not Found\u201d. This will be treated as a soft 404 since the actual status code returned will be a 200 okay.<\/li><\/ol>\n\n\n\n<h3>Sitemap<\/h3>\n\n\n\n<p>JavaScript frameworks typically have routers that map to clean URLs. These routers usually have an additional module that can also create sitemaps. You can find them by searching for your system + router sitemap, such as \u201cVue router sitemap.\u201d Many of the rendering solutions may also have sitemap options. Again, just find the system you use and Google the system + sitemap such as \u201cGatsby sitemap\u201d and you\u2019re sure to find a solution that already exists.<\/p>\n\n\n\n<h3>Redirects<\/h3>\n\n\n\n<p>SEOs are used to&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/301-redirects\/\">301\/302 redirects<\/a>&nbsp;, which are server-side. But Javascript is typically run client-side. This is okay since Google processes the page as follows the redirect. The redirects still pass all signals like&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/google-pagerank\/\">PageRank<\/a>. You can usually find these redirects in the code by looking for \u201cwindow.location.href\u201d.<\/p>\n\n\n\n<h3>Internationalization<\/h3>\n\n\n\n<p>There are usually a few module options for different frameworks that support some features needed for internationalization like&nbsp;<a href=\"https:\/\/ahrefs.com\/blog\/hreflang-tags\/\">hreflang<\/a>. They\u2019ve commonly been ported to the different systems and include i18n, intl, or many times the same modules used for header tags like Helmet can be used to add needed tags.<\/p>\n\n\n\n<h3>Lazy loading<\/h3>\n\n\n\n<p>There are usually modules for handling&nbsp;<a href=\"https:\/\/developers.google.com\/search\/docs\/guides\/lazy-loading\" target=\"_blank\" rel=\"noreferrer noopener\">lazy loading<\/a>. If you haven\u2019t noticed yet, there are modules to handle pretty much everything you need to do when working with JavaScript frameworks. Lazy and Suspense are the most popular modules for lazy loading. You\u2019ll want to lazy load images, but be careful not to lazy load content. This can be done with JavaScript, but it might mean that it\u2019s not picked up correctly by search engines.<\/p>\n\n\n\n<h2>Final thoughts<\/h2>\n\n\n\n<p>JavaScript is a tool to be used wisely, not something for SEOs to fear. Hopefully, this article has helped you understand how to work with it better, but don\u2019t be afraid to reach out to your developers and work with them and ask them questions. They are going to be your greatest allies in helping to improve your JavaScript site for search engines.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Did you know that while Ahrefs blog is powered by WordPress, much of the rest of the site is powered by JavaScript like React?<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[29],"tags":[42],"_links":{"self":[{"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/posts\/1139"}],"collection":[{"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1139"}],"version-history":[{"count":1,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/posts\/1139\/revisions"}],"predecessor-version":[{"id":1140,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=\/wp\/v2\/posts\/1139\/revisions\/1140"}],"wp:attachment":[{"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1139"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1139"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lvboard.infostore.in.ua\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1139"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}