<?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: Emmanuel Vingadassalon</title>
    <description>The latest articles on DEV Community by Emmanuel Vingadassalon (@emmanuel_vingadassalon_41).</description>
    <link>https://dev.to/emmanuel_vingadassalon_41</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%2F4198828%2Fa39939f1-a0ef-416e-9cd7-73d736d93027.jpg</url>
      <title>DEV Community: Emmanuel Vingadassalon</title>
      <link>https://dev.to/emmanuel_vingadassalon_41</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/emmanuel_vingadassalon_41"/>
    <language>en</language>
    <item>
      <title>What I learned building 20 small web-checking tools for the Apify Store</title>
      <dc:creator>Emmanuel Vingadassalon</dc:creator>
      <pubDate>Wed, 21 Oct 2026 19:27:06 +0000</pubDate>
      <link>https://dev.to/emmanuel_vingadassalon_41/what-i-learned-building-20-small-web-checking-tools-for-the-apify-store-nb0</link>
      <guid>https://dev.to/emmanuel_vingadassalon_41/what-i-learned-building-20-small-web-checking-tools-for-the-apify-store-nb0</guid>
      <description>&lt;p&gt;I'm a computer science student in France. Over the last two weeks I built and published 20 small "Actors" (serverless tools) on the Apify Store: SSL certificate checks, broken link crawling, SPF/DMARC audits, website screenshots, OCR, sitemap extraction, brand colors and a few more. Each one is pay-per-use, usually between $0.50 and $2 per 1,000 checks.&lt;/p&gt;

&lt;p&gt;Writing the happy path was the easy part. Here are the bugs that only showed up when I ran the tools against real websites.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. A "broken link" is often just a bot protection
&lt;/h2&gt;

&lt;p&gt;My broken link crawler reported 47 broken internal links on a big French news site. None of them were broken. After a few requests, the site started answering &lt;code&gt;402 Payment Required&lt;/code&gt; and &lt;code&gt;406 Not Acceptable&lt;/code&gt; to every robot.&lt;/p&gt;

&lt;p&gt;False positives are the worst possible failure for a link checker, so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;401, 402, 403, 406, 429, 999&lt;/code&gt; are now reported as &lt;code&gt;blocked&lt;/code&gt;, not &lt;code&gt;broken&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;after 10 bot-protection answers in a row, the crawler leaves the site alone and writes one free summary row;&lt;/li&gt;
&lt;li&gt;it respects robots.txt and its &lt;code&gt;Crawl-delay&lt;/code&gt;, with at most 2 requests per second per site.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Don't charge for an empty page
&lt;/h2&gt;

&lt;p&gt;A screenshot of a big news site came back as a white page saying "Unknown Error", with HTTP status 200. It was billed like a real screenshot. Now an error page (HTTP 400+) or an empty page (no title, almost no text, no image) is still saved, but not charged, with the reason in the output.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Every URL input is an SSRF risk
&lt;/h2&gt;

&lt;p&gt;If users can give any URL, someone will try &lt;code&gt;http://169.254.169.254/&lt;/code&gt; or a public hostname that resolves to &lt;code&gt;127.0.0.1&lt;/code&gt;. Checking the hostname before the request is not enough (DNS rebinding). The check has to run on the IP address actually used for the connection: a custom DNS lookup in undici, a local proxy for headless Chrome, and a custom network backend in Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. OCR needs a white margin
&lt;/h2&gt;

&lt;p&gt;Tesseract found zero words in a GIF with one clear word on a white background. It needed a white border around the image. Transparent PNGs were worse: converted to grayscale, the text drawn with the alpha channel became a black page. The fix is to flatten transparency onto white, then add a margin.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Next.js is hard to detect without running JavaScript
&lt;/h2&gt;

&lt;p&gt;The Wappalyzer fingerprints detect Next.js through &lt;code&gt;__NEXT_DATA__&lt;/code&gt;, which the App Router no longer sets. Reading only HTML and headers, nextjs.org itself was not detected. &lt;code&gt;/_next/static/&lt;/code&gt; scripts, &lt;code&gt;self.__next_f.push&lt;/code&gt; and &lt;code&gt;x-nextjs-*&lt;/code&gt; headers do the job.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Test with hard inputs, not just examples
&lt;/h2&gt;

&lt;p&gt;What found most of these bugs: running every tool on purpose-made hard inputs (IDN domains, subdomains, bot-protected sites, transparent images, several sites in one run) and reading every output row, not only the run status.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tools
&lt;/h2&gt;

&lt;p&gt;All of them are on my Apify profile: &lt;a href="https://apify.com/propped_cloak" rel="noopener noreferrer"&gt;https://apify.com/propped_cloak&lt;/a&gt;. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://apify.com/propped_cloak/broken-link-crawler" rel="noopener noreferrer"&gt;Broken Link Checker&lt;/a&gt;: crawl a whole site, with the page where each dead link was found, and an HTML report&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://apify.com/propped_cloak/ssl-certificate-checker" rel="noopener noreferrer"&gt;SSL Certificate Checker&lt;/a&gt;: expiry dates of hundreds of hosts&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://apify.com/propped_cloak/email-security-checker" rel="noopener noreferrer"&gt;Email Security Checker&lt;/a&gt;: SPF, DKIM, DMARC with a grade&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://apify.com/propped_cloak/website-color-palette" rel="noopener noreferrer"&gt;Website Color Palette&lt;/a&gt;: brand colors of any site, with paste-ready CSS variables and a Tailwind v4 theme&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://apify.com/propped_cloak/web-page-to-markdown" rel="noopener noreferrer"&gt;Web Page to Markdown&lt;/a&gt;: clean Markdown for LLMs and RAG&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They work from the Apify API, on a schedule, and from AI agents through the Apify MCP server. Feedback is welcome, especially bug reports.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was drafted with AI assistance and reviewed by me.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>apify</category>
      <category>webdev</category>
      <category>seo</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
