<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: vunelix</title>
    <description>The latest articles on DEV Community by vunelix (@vunelix).</description>
    <link>https://dev.to/vunelix</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3796755%2Fad40ba1f-4218-4456-9236-9a719882d324.png</url>
      <title>DEV Community: vunelix</title>
      <link>https://dev.to/vunelix</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vunelix"/>
    <language>en</language>
    <item>
      <title>Googlebot Saw Empty Tables on Our JavaScript Site: What We Found in Search Console</title>
      <dc:creator>vunelix</dc:creator>
      <pubDate>Sat, 31 Oct 2026 08:04:54 +0000</pubDate>
      <link>https://dev.to/vunelix/googlebot-saw-empty-tables-on-our-javascript-site-what-we-found-in-search-console-14gc</link>
      <guid>https://dev.to/vunelix/googlebot-saw-empty-tables-on-our-javascript-site-what-we-found-in-search-console-14gc</guid>
      <description>&lt;p&gt;Our site is a set of market-data tools: screeners, heatmaps and calculators. Tables are filled by JavaScript from a small backend endpoint. For months Search Console showed "Crawled - currently not indexed" for almost every tool page, and we blamed content quality. Then we looked at two reports we had never opened, and found two plain technical bugs.&lt;/p&gt;

&lt;p&gt;This post is from the Vunelix team and the site in question is ours. The lessons apply to any JavaScript-rendered site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug 1: 96% of Googlebot's requests went to one endpoint that always failed
&lt;/h2&gt;

&lt;p&gt;In Search Console, open &lt;strong&gt;Settings → Crawl stats → By response&lt;/strong&gt;. Ours showed about 7.8 million Googlebot requests in 90 days, and 96% of them were "Other client error (4XX)". The example URLs were all the same path: a POST-only endpoint that our embeddable widgets call.&lt;/p&gt;

&lt;p&gt;The reason: our widgets run on other people's sites. Googlebot renders those pages too, runs the widget script, and posts to our endpoint about 80,000 times a day. Every call was rejected (correctly, because Googlebot is not an allowed domain), so it was pure waste.&lt;/p&gt;

&lt;p&gt;The fix was one line in &lt;code&gt;robots.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;Disallow&lt;/span&gt;: /&lt;span class="n"&gt;embed&lt;/span&gt;/&lt;span class="n"&gt;render&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have a POST endpoint that is only meant for scripts, check whether crawlers are hammering it. Crawl stats will tell you in a minute.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug 2: our own pages looked empty to Googlebot
&lt;/h2&gt;

&lt;p&gt;We had recently moved all data requests behind a small proxy that accepts only requests that look like a real browser: it checks headers such as &lt;code&gt;Sec-Fetch-Site&lt;/code&gt;, &lt;code&gt;Sec-Fetch-Dest&lt;/code&gt; and &lt;code&gt;Accept-Language&lt;/code&gt;. That is good against scrapers. But Googlebot's renderer does not send those headers, so the proxy returned 403 and every table stayed as a loading skeleton.&lt;/p&gt;

&lt;p&gt;We found it with the one tool that shows what Google really sees:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search Console → &lt;strong&gt;URL Inspection&lt;/strong&gt; → enter the page&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test live URL&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;View tested page&lt;/strong&gt; → the &lt;strong&gt;HTML&lt;/strong&gt; tab (this is the rendered DOM, after JavaScript)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More info&lt;/strong&gt; → &lt;strong&gt;Page resources&lt;/strong&gt; lists every request that failed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our stock screener had 1 table row in the rendered HTML instead of 51. We had been looking at the page in a browser, where it was fine, for weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: verify the bot, do not trust the User-Agent
&lt;/h2&gt;

&lt;p&gt;You cannot just allow anything whose User-Agent says "Googlebot": a scraper can write that string in one line. Google documents two ways to verify a crawler:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;check the IP against the &lt;strong&gt;published IP range files&lt;/strong&gt; (Google and Bing both publish JSON lists of prefixes, IPv4 and IPv6)&lt;/li&gt;
&lt;li&gt;or do a &lt;strong&gt;reverse DNS lookup&lt;/strong&gt;, check the hostname ends in &lt;code&gt;googlebot.com&lt;/code&gt; or &lt;code&gt;google.com&lt;/code&gt;, then do a forward lookup and confirm it returns the same IP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our first version used only reverse DNS. In live tests it worked most of the time, but sometimes the table was empty again. One failed or slow lookup for a fresh Google IP was cached as "not a bot" and blocked that IP for an hour. The second version checks the published IP ranges first (no DNS needed, answers instantly), uses DNS only as a fallback, and caches an uncertain result for just a minute. After that, every live test returned the full table.&lt;/p&gt;

&lt;p&gt;A few practical points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cache the IP-range files for a day, and keep using the old file if a fetch fails.&lt;/li&gt;
&lt;li&gt;Treat "DNS failed" and "DNS says it is not Google" as different results. Only the second is evidence.&lt;/li&gt;
&lt;li&gt;Log each new decision (IP, engine, method, result). When something goes wrong later, you will want to see why.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to test your own site
&lt;/h2&gt;

&lt;p&gt;Run &lt;strong&gt;Test live URL&lt;/strong&gt; more than once. Our failures were intermittent, and a single good run proves nothing. Look at the rendered HTML, not at the screenshot.&lt;/p&gt;

&lt;p&gt;You can see the result on any of our tool pages, for example the &lt;a href="https://vunelix.com/stock-screener" rel="noopener noreferrer"&gt;stock screener&lt;/a&gt; or the &lt;a href="https://vunelix.com/crypto-cex-screener" rel="noopener noreferrer"&gt;crypto CEX screener&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Indexing is still slow for a new site with a very large number of near-duplicate pages, and these fixes only remove our own obstacles; they do not force Google to index anything. But it is much better to know that Google sees your content than to wonder.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>php</category>
    </item>
    <item>
      <title>Add a live stock or crypto ticker to WordPress without an API key</title>
      <dc:creator>vunelix</dc:creator>
      <pubDate>Sat, 03 Oct 2026 08:42:31 +0000</pubDate>
      <link>https://dev.to/vunelix/add-a-live-stock-or-crypto-ticker-to-wordpress-without-an-api-key-3314</link>
      <guid>https://dev.to/vunelix/add-a-live-stock-or-crypto-ticker-to-wordpress-without-an-api-key-3314</guid>
      <description>&lt;p&gt;If you run a finance blog, a crypto news site or an investing newsletter on WordPress, sooner or later you want live prices on the page: a scrolling ticker in the header, a Bitcoin price next to an article, a stock screener on its own page.&lt;/p&gt;

&lt;p&gt;The usual options are an iframe from a charting site, or a market-data API that needs a key, a server-side cache and some JavaScript you now have to maintain. This post shows a third way: a free plugin that renders the widgets from a shortcode, with no API key and no iframe.&lt;/p&gt;

&lt;p&gt;This post is from the Vunelix team, so the plugin is our own. It sticks to the practical steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/vunelix-financial-widgets/" rel="noopener noreferrer"&gt;Vunelix Financial Widgets&lt;/a&gt; is a free plugin on WordPress.org with 40+ widget types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tickers&lt;/strong&gt;: scrolling ticker tape, static price cards, a single-symbol card, and an inline "ticker tag" that sits inside a sentence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Screeners&lt;/strong&gt;: crypto, stocks, forex, commodities, indices, funds, CEX and DEX pairs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heatmaps&lt;/strong&gt;: crypto, stock and currency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Charts&lt;/strong&gt;: symbol overview, mini chart, seasonality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calendars&lt;/strong&gt;: economic calendar and dividends calendar&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tools&lt;/strong&gt;: currency converter, cross rates, gold calculator, technical analysis gauges&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prices update live over WebSocket. Each widget renders inside Shadow DOM, so your theme's CSS cannot break it and it cannot break your theme.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Install the plugin
&lt;/h2&gt;

&lt;p&gt;In your WordPress admin go to &lt;strong&gt;Plugins → Add New&lt;/strong&gt;, search for "Vunelix Financial Widgets", then &lt;strong&gt;Install Now&lt;/strong&gt; and &lt;strong&gt;Activate&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Verify your domain
&lt;/h2&gt;

&lt;p&gt;There is no API key. Widgets are authorized by domain instead, so you &lt;a href="https://developers.vunelix.com/signup" rel="noopener noreferrer"&gt;sign up for free&lt;/a&gt; on the developer portal and add your site's domain. That is the only setup outside WordPress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Build a widget and copy the shortcode
&lt;/h2&gt;

&lt;p&gt;Open &lt;strong&gt;Vunelix Widgets&lt;/strong&gt; in the admin sidebar, pick a widget, set the options (symbols, theme, colors, language) and copy the shortcode it generates.&lt;/p&gt;

&lt;p&gt;A scrolling crypto ticker looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;[vunelix widget="ticker-tape" theme="dark" symbols="BINANCE:BTCUSD,BINANCE:ETHUSD,BINANCE:SOLUSD"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A stock screener with 50 rows for the US market:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[vunelix widget="stock-screener" per_page="50" default_country="US"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A USD to EUR converter with a chart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;[vunelix widget="currency-converter" from="USD" to="EUR" show_chart="true"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technical analysis for Apple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[vunelix widget="technical-analysis" symbol="NASDAQ:AAPL" default_period="1d"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Paste it where you want it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Block editor (Gutenberg):&lt;/strong&gt; add a &lt;strong&gt;Shortcode&lt;/strong&gt; block and paste the shortcode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Classic editor:&lt;/strong&gt; paste it straight into the content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elementor, Divi, WPBakery:&lt;/strong&gt; use the page builder's shortcode or text element.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sidebar or footer:&lt;/strong&gt; use a Shortcode widget in any widget area.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is it. Widgets are lazy loaded: the JavaScript for a widget runs when it scrolls into view.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few things worth knowing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Theme and colors.&lt;/strong&gt; Every widget can follow your site's light or dark mode automatically, or you can force one. Positive, negative, background, text and border colors can be overridden, with separate values for light and dark.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Languages.&lt;/strong&gt; Widgets are translated into English, Arabic, Spanish, Chinese, Russian, German, French and Japanese, with right-to-left layout for Arabic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Several widgets on one page.&lt;/strong&gt; Add as many as you like; each one loads on its own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not on WordPress?&lt;/strong&gt; The same widgets work on any site with a script tag. The docs and a live configurator for each widget are at &lt;a href="https://vunelix.com/widgets" rel="noopener noreferrer"&gt;vunelix.com/widgets&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the data comes from
&lt;/h2&gt;

&lt;p&gt;Prices, technical signals and chart data come from FCS API, delivered through the Vunelix backend. The plugin's readme on WordPress.org lists exactly what is sent to which service, and the JavaScript ships un-obfuscated if you want to read it.&lt;/p&gt;

&lt;p&gt;If you try it and something does not work with your theme or page builder, the &lt;a href="https://wordpress.org/support/plugin/vunelix-financial-widgets/" rel="noopener noreferrer"&gt;support forum on WordPress.org&lt;/a&gt; is the fastest way to reach us.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>fintech</category>
    </item>
  </channel>
</rss>
