<?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: Nitesh </title>
    <description>The latest articles on DEV Community by Nitesh  (@niteshg).</description>
    <link>https://dev.to/niteshg</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%2F4176684%2Fec21a55e-ea6c-4c60-a310-69a39f113f86.png</url>
      <title>DEV Community: Nitesh </title>
      <link>https://dev.to/niteshg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/niteshg"/>
    <language>en</language>
    <item>
      <title>I built 16 free PDF tools in a month — here's what surprised me</title>
      <dc:creator>Nitesh </dc:creator>
      <pubDate>Sun, 11 Oct 2026 16:14:45 +0000</pubDate>
      <link>https://dev.to/niteshg/i-built-16-free-pdf-tools-in-a-month-heres-what-surprised-me-3cna</link>
      <guid>https://dev.to/niteshg/i-built-16-free-pdf-tools-in-a-month-heres-what-surprised-me-3cna</guid>
      <description>&lt;p&gt;I built &lt;a href="https://convertpdfone.com" rel="noopener noreferrer"&gt;PDF One&lt;/a&gt; — a free online PDF studio... (compress, merge, split, PDF↔Word/Excel, protect, rotate, delete pages, and more). No signup, no watermark, no file size limit under 50 MB.&lt;/p&gt;

&lt;p&gt;I'm a solo developer. This wasn't a startup — it was a side project to learn production Flask deployment and get better at shipping. Here are the things that genuinely surprised me.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The tools were the easy part
I expected the PDF manipulation to be the hard part. It wasn't. pypdf, pdfplumber, openpyxl, and reportlab handle 90% of the cases. Each tool took a few hours to build and test.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The hard part was everything around the tools: Google OAuth, Razorpay signature verification, Postgres migrations, rate limiting, keeping the deploy pipeline from breaking every time I pushed. That's the 80%.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client-side processing changes everything
Rotate Pages and Delete Pages run entirely in the browser using pdf.js and pdf-lib. The file never leaves the user's device.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;p&gt;Zero server cost — no processing, no storage&lt;/p&gt;

&lt;p&gt;Instant — no upload time&lt;/p&gt;

&lt;p&gt;Honest privacy claim — the file literally cannot be leaked&lt;/p&gt;

&lt;p&gt;Downside: browsers block programmatic file pickers. You can't open the picker with input.click() unless it's triggered by a real user gesture. So my /rotate-pages landing page links to the homepage where the user clicks one more time.&lt;/p&gt;

&lt;p&gt;Three extra lines of code, one extra click for the user, huge win in bandwidth and trust.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Office document conversion on Linux is a trap
Excel to PDF works — I use openpyxl + reportlab, both pure Python.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Word to PDF and PowerPoint to PDF do not work on Linux unless you install LibreOffice headless (adds ~200 MB to your Docker image). I tried it. It works, but it's slow on a free tier and eats memory.&lt;/p&gt;

&lt;p&gt;For those two tools, I now return a friendly message: "This tool requires Microsoft Word. Please use File → Save As → PDF instead." Users don't complain when the message is clear.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rate limiting is not optional
I put flask-limiter on every sensitive route:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;python&lt;br&gt;
@limiter.limit("5 per minute")&lt;br&gt;
def login(): ...&lt;/p&gt;

&lt;p&gt;@limiter.limit("10 per minute")&lt;br&gt;
def word_to_pdf(): ...&lt;br&gt;
Without this, one script can DoS you. flask-limiter is 3 lines of setup. Just do it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Payment verification is where you lose money
I originally had this:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;python&lt;br&gt;
@app.route('/activate-vip', methods=['POST'])&lt;br&gt;
def activate_vip():&lt;br&gt;
    cursor.execute("UPDATE users SET is_vip = 1 WHERE id = ?", (current_user.id,))&lt;br&gt;
Any logged-in user could open DevTools and grant themselves VIP for free:&lt;/p&gt;

&lt;p&gt;js&lt;br&gt;
fetch('/activate-vip', {method: 'POST'})&lt;br&gt;
The fix is one Razorpay call:&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
razorpay_client.utility.verify_payment_signature({&lt;br&gt;
    'razorpay_order_id': order_id,&lt;br&gt;
    'razorpay_payment_id': payment_id,&lt;br&gt;
    'razorpay_signature': signature,&lt;br&gt;
})&lt;br&gt;
Verify first, then activate. Never the other way around.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SQLite on Render will betray you
Render's filesystem is ephemeral. Every deploy wipes the disk. My first 3 deploys reset the user database without warning.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your app has any user state, use a managed Postgres from day one. Neon's free tier is generous and reliable.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SEO is not a code problem
I have 16 tool pages, each targeting a specific search term ("compress pdf under 200kb", "pdf to excel converter", "merge pdf files"). Each page has:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Unique title and meta description&lt;/p&gt;

&lt;p&gt;Real 300+ word content&lt;/p&gt;

&lt;p&gt;FAQ schema in JSON-LD&lt;/p&gt;

&lt;p&gt;Internal links to related tools&lt;/p&gt;

&lt;p&gt;I submitted the sitemap to Google. Now I wait. The technical work is done. Everything from here is patience and backlinks.&lt;/p&gt;

&lt;p&gt;What I'd do differently&lt;br&gt;
Start with Postgres. Skip the SQLite phase entirely.&lt;/p&gt;

&lt;p&gt;Verify payments before writing the frontend. Retrofitting this was annoying.&lt;/p&gt;

&lt;p&gt;Build the SEO landing pages as you build each tool. Not at the end.&lt;/p&gt;

&lt;p&gt;Don't fight Notepad. Use Python scripts to edit Python files. Sounds silly, saves hours.&lt;/p&gt;

&lt;p&gt;The stack&lt;br&gt;
Flask + Gunicorn&lt;/p&gt;

&lt;p&gt;Neon Postgres&lt;/p&gt;

&lt;p&gt;Cloudflare for DNS&lt;/p&gt;

&lt;p&gt;Render for hosting&lt;/p&gt;

&lt;p&gt;pypdf, pdfplumber, openpyxl, reportlab for the tools&lt;/p&gt;

&lt;p&gt;If you've built something similar, I'd love to hear what surprised you. And if you need a free PDF tool, PDF One is live — no signup required.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>python</category>
      <category>saas</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
