<?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: Atabek Murtazaev</title>
    <description>The latest articles on DEV Community by Atabek Murtazaev (@atabek_murtazaev_18e1ac50).</description>
    <link>https://dev.to/atabek_murtazaev_18e1ac50</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%2F3902074%2F14eb716a-4fa1-41c8-b23e-b52923e28abf.jpg</url>
      <title>DEV Community: Atabek Murtazaev</title>
      <link>https://dev.to/atabek_murtazaev_18e1ac50</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/atabek_murtazaev_18e1ac50"/>
    <language>en</language>
    <item>
      <title>One React component cost me 98 unindexed pages</title>
      <dc:creator>Atabek Murtazaev</dc:creator>
      <pubDate>Mon, 12 Oct 2026 12:06:23 +0000</pubDate>
      <link>https://dev.to/atabek_murtazaev_18e1ac50/one-react-component-cost-me-98-unindexed-pages-39aa</link>
      <guid>https://dev.to/atabek_murtazaev_18e1ac50/one-react-component-cost-me-98-unindexed-pages-39aa</guid>
      <description>&lt;p&gt;Google had indexed 21 pages of my site. Another 98 sat in Search Console under &lt;strong&gt;Discovered - currently not indexed&lt;/strong&gt;, and the number was climbing.&lt;/p&gt;

&lt;p&gt;I spent a week on the wrong explanations. Thin content, maybe. A new domain with no authority. Too many similar pages. All plausible, all wrong.&lt;/p&gt;

&lt;p&gt;The cause was four characters in a React component.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Symptom
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;-&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;th&gt;Pages&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Discovered — currently not indexed&lt;/td&gt;
&lt;td&gt;98&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Page with redirect&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Alternate page with canonical tag&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Excluded by noindex tag&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Duplicate without user-selected canonical&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Crawled — currently not indexed&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Everything except that first row is benign. The redirects and canonicals are working as intended. The 98 are the problem, and the label is precise in a way that’s easy to skim past.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discovered means Google knows the URL exists&lt;/strong&gt;. It found it in my sitemap.&lt;br&gt;
&lt;strong&gt;Currently not indexed means&lt;/strong&gt; it chose not to spend crawl budget fetching it.&lt;/p&gt;

&lt;p&gt;Google has not read those pages, so this isn’t a verdict on the content. It decided they weren’t worth fetching, and the strongest signal I can control there is internal links.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Cause
&lt;/h2&gt;

&lt;p&gt;The piece of code that&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isOpen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;open&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;{isOpen &amp;amp;&amp;amp; &amp;lt;nav&amp;gt;}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If the category is closed, the nav is not hidden. It does not exist. React never creates the element, so the links are not in the DOM, so they are not in the HTML a crawler receives.&lt;/p&gt;

&lt;p&gt;One category is open by default. Five links. The other 25 tool pages had zero links from the homepage - the only page on the site with any external authority at all.&lt;/p&gt;

&lt;p&gt;I confirmed it the way I should have started, by reading what is actually served rather than what the browser shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://papyrio.net/ | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'href="/[a-z-]*-pdf"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How I solved this problem
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="na"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;hidden is an HTML attribute. The element renders, the links are in the markup, the browser does not paint it. Crawlers read markup.&lt;/p&gt;

&lt;p&gt;Add aria-expanded on the button while you are there, so the state is exposed to assistive technology rather than implied by what is missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;-&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Tool links on the homepage&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Tool pages with no internal links&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Internal links site-wide&lt;/td&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;722&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why this is easy to miss
&lt;/h2&gt;

&lt;p&gt;I found these 3 hidden areas&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Everything worked.&lt;/strong&gt; Not a bug. No error, no warning, no failing test. The&lt;br&gt;
accordion behaved exactly as designed in every browser I opened.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My tooling agreed.&lt;/strong&gt; Lighthouse was green. The pages had titles, meta&lt;br&gt;
descriptions, canonical tags, hreflang, structured data. Every SEO checklist&lt;br&gt;
item I could tick was ticked. None of them check whether the page is reachable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The browser lied to me.&lt;/strong&gt; Inspecting the homepage in DevTools, I saw thirty&lt;br&gt;
links, because interacting with a page is how you end up looking at it. The&lt;br&gt;
crawler never clicks anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  My next analyze
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;curl&lt;/code&gt; your pages and count the links.&lt;/strong&gt; Not DevTools. Not "view source"
after you have clicked around. A cold fetch, the way a crawler sees it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grep for &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; before JSX that contains a &lt;code&gt;Link&lt;/code&gt;.&lt;/strong&gt; Conditional rendering
and conditional &lt;em&gt;visibility&lt;/em&gt; are different things, and the first one deletes
content from the document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check tabs, modals, carousels and "load more" buttons.&lt;/strong&gt; Same pattern,
same consequence. Anything that mounts on interaction is invisible to a
crawler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the Search Console reason strings literally.&lt;/strong&gt; "Discovered" and
"Crawled" are different diagnoses. The first is a linking problem, the second
is a content problem. I treated a linking problem as a content problem for a
week.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  I still annoy on this part
&lt;/h2&gt;

&lt;p&gt;The actual fix was &lt;code&gt;{isOpen &amp;amp;&amp;amp; &amp;lt;nav&amp;gt;}&lt;/code&gt; to &lt;code&gt;&amp;lt;nav hidden={!isOpen}&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;By the way I forgot to mention the product. &lt;a href="https://papyrio.net/" rel="noopener noreferrer"&gt;papyrio&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading !!!&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>go</category>
      <category>nextjs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>My PDF compressor was making files bigger</title>
      <dc:creator>Atabek Murtazaev</dc:creator>
      <pubDate>Sun, 04 Oct 2026 11:09:41 +0000</pubDate>
      <link>https://dev.to/atabek_murtazaev_18e1ac50/my-pdf-compressor-was-making-files-bigger-2ej2</link>
      <guid>https://dev.to/atabek_murtazaev_18e1ac50/my-pdf-compressor-was-making-files-bigger-2ej2</guid>
      <description>&lt;p&gt;I run a free PDF toolkit. Last month I checked something I had never actually measured: whether the compress tool compressed anything.&lt;/p&gt;

&lt;p&gt;It did not. A &lt;strong&gt;2,879,606-byte&lt;/strong&gt; PDF came back at &lt;strong&gt;2,938,188 bytes&lt;/strong&gt;. Two percent larger. The meta description on that page promised &lt;em&gt;image-heavy PDFs shrink 30-70%&lt;/em&gt;, and it had been promising that to every visitor for months. Examples written in &lt;strong&gt;Go&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The cause
&lt;/h2&gt;

&lt;p&gt;The handler sent the file to Gotenberg asking for PDF/A-2b:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pdfa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"PDF/A-2b"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PDF/A is an archival format. It embeds every font and ICC colour profile and leaves images at full resolution, so the file still renders the same way in forty years. I read "PDF/A" and pattern-matched: A for Archive, archives are small.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;Ghostscript, which actually resamples images:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="s"&gt;"gs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="s"&gt;"-dBATCH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-dNOPAUSE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-dQUIET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="s"&gt;"-sDEVICE=pdfwrite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="s"&gt;"-dCompatibilityLevel=1.7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="s"&gt;"-dPDFSETTINGS="&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        
         &lt;span class="s"&gt;"-dDetectDuplicateImages=true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="s"&gt;"-dCompressFonts=true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-dSubsetFonts=true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="s"&gt;"-dDownsampleColorImages=true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="s"&gt;"-dColorImageResolution="&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;dpi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="s"&gt;"-sOutputFile="&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;outPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inPath&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same file, measured against production: &lt;strong&gt;2,879,606 bytes&lt;/strong&gt; in, 417,480 out. Minus &lt;strong&gt;85.5%&lt;/strong&gt;, and 3.2 seconds instead of 16.2. Text stays selectable because fonts are subset rather than rasterized.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compressed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileBytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;compressed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileBytes&lt;/span&gt;  &lt;span class="c"&gt;// already optimized; hand back the original&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without it you ship the PDF/A bug again in a different costume. Files that are already optimized get bigger when you rebuild them, and a compression tool that sometimes inflates your file is worse than no tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then I tried to sell it
&lt;/h2&gt;

&lt;p&gt;The web app makes no money. Anonymous people convert one PDF and leave, which is the correct behaviour for a free converter and a terrible business. But the same endpoints behind an API serve a different customer - one who pays, because their invoicing system needs to merge PDFs every night.&lt;/p&gt;

&lt;p&gt;So: list it on &lt;strong&gt;RapidAPI&lt;/strong&gt;. Before writing any code, measure whether the hardware survives paying traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capacity first
&lt;/h2&gt;

&lt;p&gt;The whole thing runs on one &lt;strong&gt;t3.micro&lt;/strong&gt;. That is a burstable instance: &lt;br&gt;
you get a baseline CPU allocation and a credit balance you spend when you exceed it. Run out of credits and you either throttle hard or start paying surplus charges. &lt;/p&gt;

&lt;p&gt;I ramped concurrent conversions and watched &lt;strong&gt;CPUCreditBalance&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;144 conversions per hour sustained without touching the credit
balance &lt;/li&gt;
&lt;li&gt;5 concurrent requests comfortable, degrading past that&lt;/li&gt;
&lt;li&gt;credit balance stayed full throughout&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is small. It is also more than enough - &lt;strong&gt;144 per hour&lt;/strong&gt; is &lt;strong&gt;3,456 per day&lt;/strong&gt;, and the paid tiers I planned top out at &lt;strong&gt;25,000 per month&lt;/strong&gt;. The go or no-go answered itself, and it cost an afternoon instead of a migration.&lt;/p&gt;
&lt;h2&gt;
  
  
  The bug that would have broken every paying customer
&lt;/h2&gt;

&lt;p&gt;RapidAPI proxies all traffic through its own IP addresses.&lt;/p&gt;

&lt;p&gt;My rate limiter was keyed on client IP. Ten requests per minute, per IP, which is sensible for a public web app. Behind the gateway, every paying subscriber in the world shares the same handful of proxy IPs. Customer A's requests would have exhausted Customer B's limit. The first two subscribers would have throttled each other into &lt;strong&gt;429s&lt;/strong&gt; and churned, and I would have had no idea why.&lt;/p&gt;

&lt;p&gt;The fix is to key on the subscriber instead, and to verify the request actually came through the gateway before trusting the header that names them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;RapidAPIAuth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ServeHTTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-RapidAPI-Proxy-Secret"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServeHTTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// not a gateway request, carry on&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;subtle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConstantTimeCompare&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;writeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusForbidden&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"forbidden"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-RapidAPI-User"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// ... attribute the request to `user`, and rate limit on that&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A limit you are not allowed to set
&lt;/h2&gt;

&lt;p&gt;I wanted paid plans at &lt;strong&gt;30 requests per hour&lt;/strong&gt;, matching what the box can actually do.&lt;/p&gt;

&lt;p&gt;RapidAPI does not let you. Paid tiers are locked at &lt;strong&gt;1,000 requests per hour&lt;/strong&gt; and the provider has no field to lower it. So the platform advertises a number my hardware cannot serve.&lt;/p&gt;

&lt;p&gt;The resolution is not technical, it is honesty. The server enforces &lt;strong&gt;120 per hour&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Better a customer reads that before subscribing than discovers it at request 121.&lt;/p&gt;

&lt;h2&gt;
  
  
  Endpoints do not exist until you declare them
&lt;/h2&gt;

&lt;p&gt;Last one, and it cost me real time.&lt;/p&gt;

&lt;p&gt;The origin server has twelve endpoints. I defined four in RapidAPI's dashboard and assumed the gateway was a dumb proxy that would forward anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://...p.rapidapi.com/ocr-pdf"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-RapidAPI-Key: &lt;/span&gt;&lt;span class="nv"&gt;$KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; ...
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;:&lt;span class="s2"&gt;"Endpoint '/ocr-pdf' does not exist"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What made this hard to catch: an undeclared path returns 401 without a key, exactly like a declared one. Authentication runs before path resolution. So the obvious probe tells you nothing, and you need a valid key to discover that eight of your twelve endpoints are unreachable.&lt;/p&gt;

&lt;p&gt;I had already written documentation for all twelve. Had I shipped it, every subscriber would have hit a 404 on two thirds of the product.&lt;/p&gt;

&lt;p&gt;You can find it on RapidAPI where 12 enpoinds live. &lt;/p&gt;

&lt;p&gt;Free tier is 50 requests a month, no card. It is on RapidAPI as Papyrio PDF Tools, and the reference is at &lt;a href="https://papyrio.net/docs" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>go</category>
      <category>programming</category>
      <category>software</category>
    </item>
  </channel>
</rss>
