<?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: Mai Chi Bao</title>
    <description>The latest articles on DEV Community by Mai Chi Bao (@mrzaizai2k).</description>
    <link>https://dev.to/mrzaizai2k</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%2F1976523%2F358f751f-fd95-4b8b-becb-99bdb7b5faa9.jpg</url>
      <title>DEV Community: Mai Chi Bao</title>
      <link>https://dev.to/mrzaizai2k</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mrzaizai2k"/>
    <language>en</language>
    <item>
      <title>Investigating a Hybrid LLM-GNN Model to Enhance the Efficiency of ADAPT-QAOA for Quantum Circuit Optimization</title>
      <dc:creator>Mai Chi Bao</dc:creator>
      <pubDate>Sat, 20 Jun 2026 13:25:00 +0000</pubDate>
      <link>https://dev.to/mrzaizai2k/investigating-a-hybrid-llm-gnn-model-to-enhance-the-efficiency-of-adapt-qaoa-for-quantum-circuit-379j</link>
      <guid>https://dev.to/mrzaizai2k/investigating-a-hybrid-llm-gnn-model-to-enhance-the-efficiency-of-adapt-qaoa-for-quantum-circuit-379j</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction: A Quantum Adventure&lt;/li&gt;
&lt;li&gt;What Is This Project About?&lt;/li&gt;
&lt;li&gt;The Problems We Face&lt;/li&gt;
&lt;li&gt;My Journey and Discoveries&lt;/li&gt;
&lt;li&gt;Results: What Did I Find?&lt;/li&gt;
&lt;li&gt;Conclusion: The Road Ahead&lt;/li&gt;
&lt;li&gt;Resources&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Introduction: A Quantum Adventure
&lt;/h2&gt;

&lt;p&gt;Welcome to the fascinating world of quantum computing! Imagine a world where we can solve complex problems in logistics, resource allocation, and network design. But to reach that world, we face significant challenges. In my journey through the realm of quantum computing, I've been working on something exciting: a framework that helps us create quantum circuits more efficiently. And let me tell you, it’s like embarking on a thrilling adventure filled with puzzles and discoveries. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F40s6inv3kzi5ibx1hnbe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F40s6inv3kzi5ibx1hnbe.png" alt=" " width="598" height="591"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is This Project About?
&lt;/h2&gt;

&lt;p&gt;At its core, my project is about solving combinatorial optimization problems using &lt;strong&gt;ADAPT-QAOA&lt;/strong&gt; (the Adaptive Quantum Approximate Optimization Algorithm). Now, that might sound like a mouthful, but think of it as a powerful recipe for making quantum circuits—circuits that can help us find the best solutions to tricky problems.&lt;/p&gt;

&lt;p&gt;In simple terms, my framework blends modern machine learning techniques with quantum computing to produce quantum circuits tailored for graph-based problems like &lt;strong&gt;Max-Cut&lt;/strong&gt;. Adapting these circuits efficiently can save time and computational resources, allowing quantum computing to shine in real-world applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problems We Face
&lt;/h2&gt;

&lt;p&gt;As I delved deeper into this project, I collected a few challenges that the quantum computing community faces when dealing with adaptive quantum circuits:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Complex Circuit Design&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Designing quantum circuits isn't as simple as following a recipe; it requires creativity and insight. The challenge is that the possibilities are almost endless—choosing the right components (or operators) can be like finding a needle in a haystack.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Parameter Initialization&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Even if we can design a great circuit, if we don’t set the parameters properly from the start, our circuit could perform poorly. Think of it like baking: if your ingredients are off, the cake won’t rise.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Limited Generalization&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Methods that work well for one graph structure often fail when faced with a different one. This lack of flexibility makes it hard to scale solutions to larger or different problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Journey and Discoveries
&lt;/h2&gt;

&lt;p&gt;On this fascinating journey, I learned that the answer lies in combining advanced techniques. I decided to integrate &lt;strong&gt;Large Language Models (LLMs)&lt;/strong&gt;, like &lt;strong&gt;Transformer networks&lt;/strong&gt;, with &lt;strong&gt;Graph Neural Networks (GNNs)&lt;/strong&gt;. This combination allows the framework to generate circuits based on learned relationships between graph structures and quantum operations.&lt;/p&gt;

&lt;p&gt;Imagine teaching a language model not to write sentences, but to generate step-by-step instructions for building quantum circuits. This makes the whole process a lot more efficient and robust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results: What Did I Find?
&lt;/h2&gt;

&lt;p&gt;After a series of experiments using graphs with 9, 10, and 11 nodes, I was thrilled to discover a treasure trove of results.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Fast Learning and Adaptation&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The model learned rapidly, with the approximation ratio climbing above 0.9 early in the training.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F889590m7m34t4ka9713f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F889590m7m34t4ka9713f.png" alt="Approximation Ratio" width="799" height="259"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Compact Circuit Designs&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;As training progressed, the model not only created good circuits but also reduced their complexity, yielding more efficient outputs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwv73mx3qvbz716d1e435.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwv73mx3qvbz716d1e435.png" alt="Number of Layers" width="799" height="246"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Stability and Scalability&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Among the architectures I tested, &lt;strong&gt;NanoGPT&lt;/strong&gt; stood out as an exceptional performer. It consistently achieved high results while keeping circuit depths manageable.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Interacting with Different Embedding Types&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I tried different graph representation techniques—&lt;strong&gt;NetLSD&lt;/strong&gt;, &lt;strong&gt;FEATHER&lt;/strong&gt;, and &lt;strong&gt;GNN&lt;/strong&gt;. Each had its strengths, with &lt;strong&gt;FEATHER&lt;/strong&gt; often leading in performance while &lt;strong&gt;NetLSD&lt;/strong&gt; offered stability.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;Surpassing Traditional Methods&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Most importantly, my framework showed that it could outperform traditional approaches like &lt;strong&gt;Vanilla QAOA&lt;/strong&gt; by achieving better approximation ratios with less computational effort.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. &lt;strong&gt;Inference Time Comparison&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A comparison of inference times illustrated the efficiency of my approach. The proposed hybrid framework maintained low and stable inference times across different graph sizes, showcasing its scalability advantage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Felu9ub6rtzuna6bxu3co.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Felu9ub6rtzuna6bxu3co.png" alt="Inference Time" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: The Road Ahead
&lt;/h2&gt;

&lt;p&gt;As I conclude this adventure, I'm filled with excitement for the road ahead. While my framework shows considerable promise, it still struggles with generalization beyond what it was trained on. However, there's so much potential for improvement!&lt;/p&gt;

&lt;p&gt;With more diverse datasets and further exploration, I believe we can refine this framework to tackle even more complex problems. The journey into quantum computing has only just begun, and it's turning out to be an exhilarating ride!&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📹 &lt;a href="https://www.youtube.com/watch?v=4bbEIj1MuAc&amp;amp;t=2s" rel="noopener noreferrer"&gt;Watch My Project Explanation on YouTube&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💻 &lt;a href="https://github.com/mrzaizai2k/Adapt-LLM" rel="noopener noreferrer"&gt;Check Out the Adapt-LLM Code on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for joining me on this journey! If you're as enthusiastic about quantum computing as I am, let’s explore this brilliant world together! Who knows? The next big discovery might just be around the corner.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>quantum</category>
      <category>llm</category>
      <category>mrzaizai2k</category>
    </item>
    <item>
      <title>I’ll be honest with you:
I didn’t wake up one morning thinking “Let me reverse-engineer TikTok today.”

It started with something much simpler:

“I just want to upload videos to TikTok programmatically…
why is this so damn hard?”</title>
      <dc:creator>Mai Chi Bao</dc:creator>
      <pubDate>Mon, 17 Nov 2025 14:05:37 +0000</pubDate>
      <link>https://dev.to/mrzaizai2k/ill-be-honest-with-you-i-didnt-wake-up-one-morning-thinking-let-me-reverse-engineer-tiktok-1pbg</link>
      <guid>https://dev.to/mrzaizai2k/ill-be-honest-with-you-i-didnt-wake-up-one-morning-thinking-let-me-reverse-engineer-tiktok-1pbg</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/mrzaizai2k/breaking-down-api-defenses-ua-cookies-signatures-browser-5f58" class="crayons-story__hidden-navigation-link"&gt;Breaking Down API Defenses: UA - Cookies - Signatures Browser 🤖&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/mrzaizai2k" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1976523%2F358f751f-fd95-4b8b-becb-99bdb7b5faa9.jpg" alt="mrzaizai2k profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/mrzaizai2k" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Mai Chi Bao
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Mai Chi Bao
                
              
              &lt;div id="story-author-preview-content-3021834" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/mrzaizai2k" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1976523%2F358f751f-fd95-4b8b-becb-99bdb7b5faa9.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Mai Chi Bao&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/mrzaizai2k/breaking-down-api-defenses-ua-cookies-signatures-browser-5f58" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Nov 17 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/mrzaizai2k/breaking-down-api-defenses-ua-cookies-signatures-browser-5f58" id="article-link-3021834"&gt;
          Breaking Down API Defenses: UA - Cookies - Signatures Browser 🤖
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/api"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;api&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mrzaizai2k"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mrzaizai2k&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/mrzaizai2k/breaking-down-api-defenses-ua-cookies-signatures-browser-5f58" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;12&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/mrzaizai2k/breaking-down-api-defenses-ua-cookies-signatures-browser-5f58#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              3&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>webdev</category>
      <category>api</category>
      <category>security</category>
      <category>mrzaizai2k</category>
    </item>
    <item>
      <title>Breaking Down API Defenses: UA - Cookies - Signatures Browser 🤖</title>
      <dc:creator>Mai Chi Bao</dc:creator>
      <pubDate>Mon, 17 Nov 2025 13:06:00 +0000</pubDate>
      <link>https://dev.to/mrzaizai2k/breaking-down-api-defenses-ua-cookies-signatures-browser-5f58</link>
      <guid>https://dev.to/mrzaizai2k/breaking-down-api-defenses-ua-cookies-signatures-browser-5f58</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I’ll be honest with you:&lt;br&gt;
I didn’t wake up one morning thinking &lt;em&gt;“Let me reverse-engineer TikTok today.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It started with something much simpler:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I just want to upload videos to TikTok programmatically…&lt;br&gt;
why is this so damn hard?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No matter what I tried — simple requests, faked headers, even stolen cookies — TikTok kept slamming the door in my face.&lt;/p&gt;

&lt;p&gt;But every failure taught me something.&lt;br&gt;
Every blocked request revealed the next security layer.&lt;/p&gt;

&lt;p&gt;So I dug in.&lt;br&gt;
I became the “hacker” TikTok didn’t want.&lt;/p&gt;

&lt;p&gt;And in this post, I’ll take you through &lt;strong&gt;the exact journey&lt;/strong&gt; — from the most naive HTTP request… all the way to &lt;strong&gt;running TikTok’s own obfuscated JavaScript in a Node VM&lt;/strong&gt; to generate browser signatures.&lt;/p&gt;

&lt;p&gt;All code here comes from my real TikTok automation stack (based on my repo:&lt;br&gt;
&lt;a href="https://github.com/mrzaizai2k/auto_tiktok/blob/main/src/tiktok_uploader/tiktok.py" rel="noopener noreferrer"&gt;github.com/mrzaizai2k/auto_tiktok&lt;/a&gt;).&lt;/p&gt;


&lt;h1&gt;
  
  
  Table of Contents
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Raw Request — My First Dumb Attempt&lt;/li&gt;
&lt;li&gt;User-Agent — “Fine, I’ll Pretend to Be Chrome”&lt;/li&gt;
&lt;li&gt;Cookies — The Real Passport&lt;/li&gt;
&lt;li&gt;Browser Signatures — When TikTok Got Serious&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;li&gt;Final Thoughts&lt;/li&gt;
&lt;li&gt;Resources&lt;/li&gt;
&lt;/ol&gt;


&lt;h1&gt;
  
  
  Raw Request
&lt;/h1&gt;
&lt;h2&gt;
  
  
  🧠 What I Tried
&lt;/h2&gt;

&lt;p&gt;The first time I tried calling TikTok’s API, I thought it would be as easy as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.tiktok.com/api/whatever&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TikTok looked at me like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Bro, who even are you?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No headers.&lt;br&gt;
No cookies.&lt;br&gt;
No browser identity.&lt;br&gt;
Instant block.&lt;/p&gt;

&lt;p&gt;To understand why, I built a minimal FastAPI to simulate what TikTok sees when a bot sends a naked request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTPException&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# no checks, no identity
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public info&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how the early internet worked.&lt;br&gt;
And this is why it died.&lt;/p&gt;


&lt;h1&gt;
  
  
  User-Agent
&lt;/h1&gt;
&lt;h3&gt;
  
  
  “I’ll wear Chrome’s clothes… surely that will work.”
&lt;/h3&gt;

&lt;p&gt;I added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My FastAPI simulation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ua&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ua&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Missing UA&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public info&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And my crawler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worked…&lt;br&gt;
…for exactly 5 minutes.&lt;/p&gt;

&lt;p&gt;Because &lt;strong&gt;any idiot can fake a UA&lt;/strong&gt;.&lt;br&gt;
Including me.&lt;/p&gt;

&lt;p&gt;So TikTok uses the next level.&lt;/p&gt;


&lt;h1&gt;
  
  
  Cookies
&lt;/h1&gt;
&lt;h2&gt;
  
  
  “Fine, TikTok, you want identity? Here’s my passport.”
&lt;/h2&gt;

&lt;p&gt;Cookies are &lt;strong&gt;small pieces of data the server sets in your browser&lt;/strong&gt;.&lt;br&gt;
When I login to TikTok on Chrome, the browser stores cookies like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sessionid&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;msToken&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tt-target-idc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;csrf_session_id&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren’t random strings — they’re TikTok’s way of saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This browser is logged in. We know this guy.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To get these cookies, I use this extension (the best one I’ve found):&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="https://www.junookyo.com/2017/07/j2team-cookies-chrome-extension.html?utm_source=extension&amp;amp;utm_medium=j2team_cookies" rel="noopener noreferrer"&gt;J2Team Cookies (Chrome)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It exports cookies into a neat JSON file — perfect for automation.&lt;/p&gt;

&lt;p&gt;Then in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cookies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_cookies_from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cookies/tiktok.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sessionid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.tiktok.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tt-target-idc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dc_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.tiktok.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now TikTok was finally willing to talk to me.&lt;br&gt;
For a while.&lt;/p&gt;

&lt;p&gt;Because cookies can still be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stolen&lt;/li&gt;
&lt;li&gt;copied&lt;/li&gt;
&lt;li&gt;reused&lt;/li&gt;
&lt;li&gt;shared&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TikTok needed something harder.&lt;/p&gt;


&lt;h1&gt;
  
  
  Browser Signatures
&lt;/h1&gt;
&lt;h2&gt;
  
  
  “Prove you’re a real browser or get lost.”
&lt;/h2&gt;

&lt;p&gt;This is where TikTok gets nasty.&lt;/p&gt;

&lt;p&gt;Even with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Valid cookies&lt;/li&gt;
&lt;li&gt;Valid User-Agent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TikTok still blocks you unless you generate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;X-Bogus&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;_signature&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;msToken&lt;/code&gt; validation&lt;/li&gt;
&lt;li&gt;device/environment fingerprints&lt;/li&gt;
&lt;li&gt;obfuscated JS crypto outputs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TikTok literally sends JavaScript that only a &lt;strong&gt;real browser&lt;/strong&gt; can execute.&lt;/p&gt;

&lt;p&gt;So to bypass it…&lt;br&gt;
I used their own JavaScript.&lt;/p&gt;
&lt;h3&gt;
  
  
  👉 Running TikTok’s JS in a Node VM
&lt;/h3&gt;

&lt;p&gt;This is my real &lt;code&gt;browser.js&lt;/code&gt; (the one doing the magic):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Browser.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Signer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./index&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;userAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&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;signer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Signer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&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;sign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&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;And this is how my Python script calls it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;subprocess_jsvmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;node&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every API request needs this signature.&lt;br&gt;
Without it — TikTok spits on you.&lt;/p&gt;

&lt;p&gt;With it — you suddenly become “a real browser.”&lt;/p&gt;

&lt;p&gt;This is exactly how I upload videos automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;signatures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;subprocess_jsvmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;js_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sig_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signatures&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;X-Bogus&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-bogus&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_signature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;signature&lt;/span&gt;&lt;span class="sh"&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;Now TikTok believes me.&lt;br&gt;
And lets me upload videos through automation.&lt;/p&gt;




&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What I Did&lt;/th&gt;
&lt;th&gt;Why It Failed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Raw request&lt;/td&gt;
&lt;td&gt;Called API directly&lt;/td&gt;
&lt;td&gt;“Who are you?”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User-Agent&lt;/td&gt;
&lt;td&gt;Pretended to be Chrome&lt;/td&gt;
&lt;td&gt;Too easy to fake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cookies&lt;/td&gt;
&lt;td&gt;Exported real TikTok cookies&lt;/td&gt;
&lt;td&gt;Reusable → insecure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser Signatures&lt;/td&gt;
&lt;td&gt;Ran TikTok’s own JS&lt;/td&gt;
&lt;td&gt;Finally works&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;There are even more authentication methods out there:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OAuth&lt;/li&gt;
&lt;li&gt;JWT tokens&lt;/li&gt;
&lt;li&gt;PKCE&lt;/li&gt;
&lt;li&gt;Rotating keys&lt;/li&gt;
&lt;li&gt;Device-bound tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want, I can continue this series:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Hit follow + drop a comment&lt;/strong&gt; if you want the next chapter.&lt;/p&gt;




&lt;h1&gt;
  
  
  Resources
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;My TikTok uploader code:
&lt;strong&gt;&lt;a href="https://github.com/mrzaizai2k/auto_tiktok/blob/main/src/tiktok_uploader/tiktok.py" rel="noopener noreferrer"&gt;auto_tiktok → tiktok.py&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>security</category>
      <category>mrzaizai2k</category>
    </item>
    <item>
      <title>Everything worked perfectly on my laptop. 🚀 The UI loaded, uploads succeeded, APIs responded instantly.

But on demo day, in front of my boss, everything collapsed. 💥</title>
      <dc:creator>Mai Chi Bao</dc:creator>
      <pubDate>Wed, 01 Oct 2025 02:55:55 +0000</pubDate>
      <link>https://dev.to/mrzaizai2k/everything-worked-perfectly-on-my-laptop-the-ui-loaded-uploads-succeeded-apis-responded-4jcj</link>
      <guid>https://dev.to/mrzaizai2k/everything-worked-perfectly-on-my-laptop-the-ui-loaded-uploads-succeeded-apis-responded-4jcj</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/mrzaizai2k" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1976523%2F358f751f-fd95-4b8b-becb-99bdb7b5faa9.jpg" alt="mrzaizai2k"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/mrzaizai2k/til-cors-ips-and-the-day-my-demo-crashed-45ce" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;TIL: CORS, IPs, and the Day My Demo Crashed 🔒&lt;/h2&gt;
      &lt;h3&gt;Mai Chi Bao ・ Sep 29 '25&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#mrzaizai2k&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#vscode&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>mrzaizai2k</category>
      <category>vscode</category>
      <category>devops</category>
    </item>
    <item>
      <title>TIL: CORS, IPs, and the Day My Demo Crashed 🔒</title>
      <dc:creator>Mai Chi Bao</dc:creator>
      <pubDate>Mon, 29 Sep 2025 12:00:00 +0000</pubDate>
      <link>https://dev.to/mrzaizai2k/til-cors-ips-and-the-day-my-demo-crashed-45ce</link>
      <guid>https://dev.to/mrzaizai2k/til-cors-ips-and-the-day-my-demo-crashed-45ce</guid>
      <description>&lt;p&gt;Everything worked perfectly on my laptop. 🚀 The UI loaded, uploads succeeded, APIs responded instantly.&lt;br&gt;&lt;br&gt;
But on demo day, in front of my boss, everything collapsed. 💥  &lt;/p&gt;

&lt;p&gt;This post is a breakdown of how &lt;strong&gt;VS Code Remote-SSH port forwarding tricked me&lt;/strong&gt;, how I fixed it, and what I learned.&lt;br&gt;&lt;br&gt;
If you’ve ever wondered why your app works fine locally but fails when someone else tries — this story might save you some embarrassment.  &lt;/p&gt;


&lt;h2&gt;
  
  
  Key Takeaways 📝
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why &lt;code&gt;localhost&lt;/code&gt; tricks you in Remote-SSH and how VS Code port forwarding can hide real network issues.
&lt;/li&gt;
&lt;li&gt;How to correctly configure backend (&lt;code&gt;0.0.0.0&lt;/code&gt;), frontend (server IP/hostname), and CORS so others can access your app.
&lt;/li&gt;
&lt;li&gt;Why testing in a clean environment (without port forwarding) is essential before demo day.
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
⚡ The Setup and Demo Day Disaster
&lt;/li&gt;
&lt;li&gt;
🔍 Chasing the Problem
&lt;/li&gt;
&lt;li&gt;
🎭 VS Code Port Forwarding: The Hidden Culprit
&lt;/li&gt;
&lt;li&gt;
🛠️ The Fix
&lt;/li&gt;
&lt;li&gt;
🔄 The CORS Twist
&lt;/li&gt;
&lt;li&gt;
✅ Conclusion
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  The Setup and Demo Day Disaster ⚡
&lt;/h2&gt;

&lt;p&gt;In my frontend &lt;code&gt;.env&lt;/code&gt; I had:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REACT_APP_API_BASE=http://localhost:6489
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BACKEND_PORT=6489
BACKEND_HOST=localhost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On my laptop (via &lt;strong&gt;VS Code Remote-SSH&lt;/strong&gt;) everything worked. UI loaded, uploads went through, API calls succeeded.&lt;/p&gt;

&lt;p&gt;But demo day came. My boss opened:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://10.X.X.X:6485
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frontend loaded, but every upload and API call failed.&lt;br&gt;
I was lost — &lt;em&gt;why does it only work on my machine, but not his?&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Chasing the Problem 🔍
&lt;/h2&gt;

&lt;p&gt;At first, I thought the backend had crashed. Logs were fine.&lt;br&gt;
Then I suspected CORS. No issues there either.&lt;/p&gt;

&lt;p&gt;Finally, I asked &lt;strong&gt;ChatGPT&lt;/strong&gt;. It suggested:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Change backend host to &lt;code&gt;0.0.0.0&lt;/code&gt; and frontend API base to server IP.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I tried it — and it worked. 🎉&lt;br&gt;
But I still didn’t understand why it only worked for me before.&lt;/p&gt;


&lt;h2&gt;
  
  
  VS Code Port Forwarding: The Hidden Culprit 🎭
&lt;/h2&gt;

&lt;p&gt;Digging deeper, I discovered the real cause:&lt;br&gt;
&lt;strong&gt;VS Code Remote-SSH was auto-forwarding my ports.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On my laptop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;localhost:6485&lt;/code&gt; → tunneled to frontend on the server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;localhost:6489&lt;/code&gt; → tunneled to backend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So for me, everything looked fine. But for my boss, &lt;code&gt;localhost&lt;/code&gt; pointed to his own machine — which had no backend at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visual proof of the trick:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before (VS Code auto port forwarding): You now can access via localhost:6485 and 10.X.X.X:6485&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpbwvjmwve3c45go2om3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpbwvjmwve3c45go2om3.png" alt=" " width="571" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Deleting ports → no more fake &lt;code&gt;localhost&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Now only server IP works:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fupg98n11rx2239314v1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fupg98n11rx2239314v1j.png" alt=" " width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Note on VS Code Port Forwarding&lt;/strong&gt;&lt;br&gt;
If you run two VS Code Remote-SSH sessions for different projects, and both try to use the same port, VS Code will automatically increment the port number.&lt;/p&gt;

&lt;p&gt;That’s why sometimes when you open &lt;code&gt;http://localhost:6485&lt;/code&gt;, it gets auto-routed to &lt;code&gt;http://localhost:6486&lt;/code&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  Why Auto Port Forwarding Matters 🔑
&lt;/h3&gt;

&lt;p&gt;When I added authentication, using the server IP broke Microsoft login — it only accepts &lt;code&gt;localhost&lt;/code&gt; or HTTPS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error when opening via server IP (&lt;code&gt;10.X.X.X:6485&lt;/code&gt;) without port forwarding:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wkx51b7j1v9d0j9iaa6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wkx51b7j1v9d0j9iaa6.png" alt=" " width="800" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; forward ports in VS Code (or manually with SSH):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-L&lt;/span&gt; 6485:localhost:6485 &lt;span class="nt"&gt;-L&lt;/span&gt; 6489:localhost:6489 user@10.X.X.X
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now login works again on &lt;code&gt;http://localhost:6485&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ig28edpxppc90gqcrh2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ig28edpxppc90gqcrh2.png" alt=" " width="794" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;But there’s a catch:&lt;/strong&gt;&lt;br&gt;
This solution is fine for local development, but not practical for demos — you can’t expect customers to forward ports.&lt;/p&gt;

&lt;p&gt;If your app relies on Microsoft login (or similar auth), you should set up &lt;strong&gt;HTTPS on the server&lt;/strong&gt;.&lt;br&gt;
That way, customers can simply access it via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://10.X.X.X:6485
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But after that, new CORS problems surfaced — which leads to the next lesson.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix 🛠️
&lt;/h2&gt;

&lt;p&gt;I fixed configs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BACKEND_PORT=6489
BACKEND_HOST=0.0.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frontend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REACT_APP_API_BASE=http://10.X.X.X:6489
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why this works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;localhost&lt;/code&gt; (127.0.0.1)&lt;/strong&gt;&lt;br&gt;
Binding your backend to &lt;code&gt;localhost&lt;/code&gt; means it only accepts connections from the same machine it’s running on.&lt;br&gt;
In other words, the server is listening only to itself. Anyone trying to reach it from another device on the network won’t be able to connect.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;0.0.0.0&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Binding to &lt;code&gt;0.0.0.0&lt;/code&gt; tells the backend to listen on &lt;strong&gt;all available network interfaces&lt;/strong&gt; (LAN, Wi-Fi, etc.).&lt;br&gt;
This makes your service accessible not just from the local machine, but also from other devices on the same network — such as your boss’s laptop during the demo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Frontend API calls&lt;/strong&gt;&lt;br&gt;
Since the frontend runs in your user’s browser, it can’t resolve your &lt;code&gt;localhost&lt;/code&gt; (that points to &lt;em&gt;their&lt;/em&gt; machine, not your server).&lt;br&gt;
The frontend must call the backend using the &lt;strong&gt;server’s IP address or hostname&lt;/strong&gt; (e.g., &lt;code&gt;http://10.X.X.X:6489&lt;/code&gt;), so that it correctly points to the server where the backend is actually running.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The CORS Twist 🔄
&lt;/h2&gt;

&lt;p&gt;Later, I caught another mistake.&lt;br&gt;
My backend allowed only &lt;code&gt;SERVER_IP&lt;/code&gt;, but when the frontend ran on a &lt;strong&gt;different machine&lt;/strong&gt; (&lt;code&gt;FRONT_END_IP&lt;/code&gt;), the requests were blocked.&lt;/p&gt;

&lt;p&gt;Fix was to add both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;FRONT_END_IP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;allowed_origins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;FRONT_END_IP&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;allowed_origins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;FRONT_END_IP&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I had lazily assumed &lt;code&gt;SERVER_IP == FRONT_END_IP&lt;/code&gt;. Wrong — and it silently broke requests.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion ✅
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;localhost&lt;/code&gt; ≠ server IP.&lt;/li&gt;
&lt;li&gt;Remote-SSH port forwarding can &lt;strong&gt;trick you&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Always bind backend to &lt;code&gt;0.0.0.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Always point frontend to server IP or hostname.&lt;/li&gt;
&lt;li&gt;Align CORS with the actual machine hosting frontend.&lt;/li&gt;
&lt;li&gt;Test from a clean environment, not your tunneled setup.&lt;/li&gt;
&lt;li&gt;And before demo day → &lt;strong&gt;disable VS Code auto port-forwarding&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Future me: no more blind trust in &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>mrzaizai2k</category>
      <category>vscode</category>
      <category>devops</category>
    </item>
    <item>
      <title>No subscriptions, no paywalls — just a straightforward QR generator that works right in your browser 🚀.</title>
      <dc:creator>Mai Chi Bao</dc:creator>
      <pubDate>Tue, 16 Sep 2025 02:46:09 +0000</pubDate>
      <link>https://dev.to/mrzaizai2k/no-subscriptions-no-paywalls-just-a-straightforward-qr-generator-that-works-right-in-your-1npg</link>
      <guid>https://dev.to/mrzaizai2k/no-subscriptions-no-paywalls-just-a-straightforward-qr-generator-that-works-right-in-your-1npg</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/mrzaizai2k/til-building-a-simple-qr-generator-5e37" class="crayons-story__hidden-navigation-link"&gt;TIL: Building a Simple QR Generator&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/mrzaizai2k" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1976523%2F358f751f-fd95-4b8b-becb-99bdb7b5faa9.jpg" alt="mrzaizai2k profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/mrzaizai2k" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Mai Chi Bao
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Mai Chi Bao
                
              
              &lt;div id="story-author-preview-content-2839292" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/mrzaizai2k" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1976523%2F358f751f-fd95-4b8b-becb-99bdb7b5faa9.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Mai Chi Bao&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/mrzaizai2k/til-building-a-simple-qr-generator-5e37" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 15 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/mrzaizai2k/til-building-a-simple-qr-generator-5e37" id="article-link-2839292"&gt;
          TIL: Building a Simple QR Generator
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tooling"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tooling&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mrzaizai2k"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mrzaizai2k&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/css"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;css&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/html"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;html&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/mrzaizai2k/til-building-a-simple-qr-generator-5e37" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/mrzaizai2k/til-building-a-simple-qr-generator-5e37#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>tooling</category>
      <category>mrzaizai2k</category>
      <category>css</category>
      <category>html</category>
    </item>
    <item>
      <title>TIL: Building a Simple QR Generator</title>
      <dc:creator>Mai Chi Bao</dc:creator>
      <pubDate>Mon, 15 Sep 2025 12:49:00 +0000</pubDate>
      <link>https://dev.to/mrzaizai2k/til-building-a-simple-qr-generator-5e37</link>
      <guid>https://dev.to/mrzaizai2k/til-building-a-simple-qr-generator-5e37</guid>
      <description>&lt;p&gt;I built a &lt;strong&gt;lightweight, open-source tool&lt;/strong&gt; to create customized QR codes.&lt;br&gt;&lt;br&gt;
No subscriptions, no paywalls — just a straightforward QR generator that works right in your browser 🚀.&lt;/p&gt;

&lt;p&gt;👉 Try it here: &lt;a href="https://mrzaizai2k.github.io/qr_generator" rel="noopener noreferrer"&gt;Simple QR Generator&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📑 Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why I Built This&lt;/li&gt;
&lt;li&gt;Features&lt;/li&gt;
&lt;li&gt;How It Works&lt;/li&gt;
&lt;li&gt;Demo Screenshot&lt;/li&gt;
&lt;li&gt;Why I Like It&lt;/li&gt;
&lt;li&gt;Reference&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💭 Why I Built This
&lt;/h2&gt;

&lt;p&gt;Most QR code tools online feel &lt;strong&gt;bloated or restrictive&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Many require &lt;strong&gt;accounts or subscriptions&lt;/strong&gt; just to download a QR code.
&lt;/li&gt;
&lt;li&gt;Some lock basic features like color, logo, or export behind a &lt;strong&gt;paywall&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Others are overloaded with ads, making them slow and frustrating.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I just wanted something &lt;strong&gt;clean, simple, and free&lt;/strong&gt; — a tool where you can generate and customize QR codes without hassle.&lt;br&gt;&lt;br&gt;
That’s why I created &lt;strong&gt;Simple QR Generator&lt;/strong&gt; — a project that anyone can use instantly in their browser.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✨ Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Generate QR codes from &lt;strong&gt;any text or URL&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Customize:

&lt;ul&gt;
&lt;li&gt;Colors&lt;/li&gt;
&lt;li&gt;Shapes&lt;/li&gt;
&lt;li&gt;Styles&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Add a &lt;strong&gt;logo&lt;/strong&gt; (upload your own or use an image URL).&lt;/li&gt;

&lt;li&gt;Adjust:

&lt;ul&gt;
&lt;li&gt;Size&lt;/li&gt;
&lt;li&gt;Margin&lt;/li&gt;
&lt;li&gt;Error correction level&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Export to &lt;strong&gt;PNG&lt;/strong&gt; or &lt;strong&gt;SVG&lt;/strong&gt;.&lt;/li&gt;

&lt;li&gt;Copy QR codes directly to clipboard.&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚡ How It Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Enter your text or URL in the input box.
&lt;/li&gt;
&lt;li&gt;Adjust customization options (color, style, size, error correction).
&lt;/li&gt;
&lt;li&gt;(Optional) Upload or link a logo to embed inside the QR code.
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Generate&lt;/strong&gt;, then either:

&lt;ul&gt;
&lt;li&gt;Export as &lt;strong&gt;PNG&lt;/strong&gt; or &lt;strong&gt;SVG&lt;/strong&gt;, or
&lt;/li&gt;
&lt;li&gt;Copy directly to clipboard for quick use.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of this happens &lt;strong&gt;entirely in your browser&lt;/strong&gt; — no server-side processing, no data collection.&lt;/p&gt;




&lt;h2&gt;
  
  
  🖥️ Demo Screenshot
&lt;/h2&gt;

&lt;p&gt;Here’s a quick look at the tool in action 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5zpmtoqhllk7lsgje9qn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5zpmtoqhllk7lsgje9qn.png" alt=" " width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can try it live here 👉 &lt;a href="https://mrzaizai2k.github.io/qr_generator" rel="noopener noreferrer"&gt;Simple QR Generator&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 Why I Like It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight&lt;/strong&gt;: works instantly in any browser.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No limits&lt;/strong&gt;: all features are free and open-source.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customizable&lt;/strong&gt;: from logos to colors, you can make QR codes match your style.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Practical&lt;/strong&gt;: whether it’s sharing a link, business info, or Wi-Fi credentials, it just works.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It started as a small side project for myself, but it turned into a neat tool worth sharing.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Reference
&lt;/h2&gt;

&lt;p&gt;All the code is open-source on my GitHub:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://github.com/mrzaizai2k/simple_qr_generator" rel="noopener noreferrer"&gt;mrzaizai2k/simple_qr_generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to clone it, star it ⭐, or suggest improvements.  &lt;/p&gt;

</description>
      <category>tooling</category>
      <category>mrzaizai2k</category>
      <category>css</category>
      <category>html</category>
    </item>
    <item>
      <title>Hi do you guys want to test?</title>
      <dc:creator>Mai Chi Bao</dc:creator>
      <pubDate>Thu, 11 Sep 2025 14:43:24 +0000</pubDate>
      <link>https://dev.to/mrzaizai2k/hi-do-you-guys-want-to-test-49og</link>
      <guid>https://dev.to/mrzaizai2k/hi-do-you-guys-want-to-test-49og</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/mrzaizai2k/til-building-a-simple-typing-practice-tool-33h8" class="crayons-story__hidden-navigation-link"&gt;TIL: Building a Simple Typing Practice Tool&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/mrzaizai2k" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1976523%2F358f751f-fd95-4b8b-becb-99bdb7b5faa9.jpg" alt="mrzaizai2k profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/mrzaizai2k" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Mai Chi Bao
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Mai Chi Bao
                
              
              &lt;div id="story-author-preview-content-2821601" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/mrzaizai2k" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1976523%2F358f751f-fd95-4b8b-becb-99bdb7b5faa9.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Mai Chi Bao&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/mrzaizai2k/til-building-a-simple-typing-practice-tool-33h8" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 8 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/mrzaizai2k/til-building-a-simple-typing-practice-tool-33h8" id="article-link-2821601"&gt;
          TIL: Building a Simple Typing Practice Tool
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tooling"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tooling&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mrzaizai2k"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mrzaizai2k&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/html"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;html&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/css"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;css&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/mrzaizai2k/til-building-a-simple-typing-practice-tool-33h8" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/mrzaizai2k/til-building-a-simple-typing-practice-tool-33h8#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              1&lt;span class="hidden s:inline"&gt;&amp;nbsp;comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>tooling</category>
      <category>mrzaizai2k</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>TIL: Building a Simple Typing Practice Tool</title>
      <dc:creator>Mai Chi Bao</dc:creator>
      <pubDate>Mon, 08 Sep 2025 13:05:00 +0000</pubDate>
      <link>https://dev.to/mrzaizai2k/til-building-a-simple-typing-practice-tool-33h8</link>
      <guid>https://dev.to/mrzaizai2k/til-building-a-simple-typing-practice-tool-33h8</guid>
      <description>&lt;h1&gt;
  
  
  📝 Today I Learned: Building a Simple Typing Practice Tool
&lt;/h1&gt;

&lt;p&gt;I built a &lt;strong&gt;really small web-based tool&lt;/strong&gt; to enhance typing skills with real-time feedback.&lt;br&gt;&lt;br&gt;
It’s just a single HTML file — no backend, no frameworks, no setup. You open it in your browser and start typing 🚀.&lt;/p&gt;




&lt;h2&gt;
  
  
  📑 Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why I Built This&lt;/li&gt;
&lt;li&gt;Features&lt;/li&gt;
&lt;li&gt;How It Works&lt;/li&gt;
&lt;li&gt;Demo Screenshot&lt;/li&gt;
&lt;li&gt;Why I Like It&lt;/li&gt;
&lt;li&gt;Reference&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💭 Why I Built This
&lt;/h2&gt;

&lt;p&gt;Most typing practice tools I tried don’t &lt;strong&gt;support Vietnamese text well&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Their UI looks broken or ugly when practicing with Vietnamese.
&lt;/li&gt;
&lt;li&gt;They rarely offer fresh, real-world Vietnamese content.
&lt;/li&gt;
&lt;li&gt;I wanted to &lt;strong&gt;auto-generate practice text&lt;/strong&gt; from news so I don’t get bored.
&lt;/li&gt;
&lt;li&gt;I also wanted to &lt;strong&gt;customize the metrics&lt;/strong&gt; I care about (e.g., word accuracy, correct WPM).
&lt;/li&gt;
&lt;li&gt;Plus, having a &lt;strong&gt;dark mode toggle&lt;/strong&gt; and a cleaner UI makes practice more enjoyable.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s why I created this simple tool — &lt;strong&gt;just for myself at first&lt;/strong&gt; — but it turned out useful enough to share.  &lt;/p&gt;




&lt;h2&gt;
  
  
  ✨ Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Works everywhere — pure HTML, CSS, and JS.&lt;/li&gt;
&lt;li&gt;Auto-fetches &lt;strong&gt;Vietnamese 🇻🇳&lt;/strong&gt; or &lt;strong&gt;English 🇺🇸&lt;/strong&gt; news text for practice.&lt;/li&gt;
&lt;li&gt;Customizable reference text (you can paste or write your own).&lt;/li&gt;
&lt;li&gt;Tracks:

&lt;ul&gt;
&lt;li&gt;Time (seconds)&lt;/li&gt;
&lt;li&gt;Character Accuracy (%)&lt;/li&gt;
&lt;li&gt;Word Accuracy (%)&lt;/li&gt;
&lt;li&gt;Words Per Minute (WPM)&lt;/li&gt;
&lt;li&gt;Correct WPM&lt;/li&gt;
&lt;li&gt;Progress (typed vs total characters)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Real-time color coding:
✅ correct, ❌ incorrect, and 🎯 current character.
&lt;/li&gt;

&lt;li&gt;Auto-scrolls while typing.&lt;/li&gt;

&lt;li&gt;Completion message when done.&lt;/li&gt;

&lt;li&gt;Dark mode + Reset button.&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚡ How It Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;You type directly on the displayed reference text.
&lt;/li&gt;
&lt;li&gt;Each keystroke updates:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Character accuracy&lt;/strong&gt; → (correct chars ÷ total typed).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Word accuracy&lt;/strong&gt; → (correct full words ÷ attempted words).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WPM&lt;/strong&gt; → counts total words per minute (including mistakes).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correct WPM&lt;/strong&gt; → only correct words per minute.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;progress bar&lt;/strong&gt; and completion message keep you motivated.
&lt;/li&gt;
&lt;li&gt;You can fetch real news text from &lt;a href="https://vnexpress.net" rel="noopener noreferrer"&gt;VnExpress&lt;/a&gt; and practice with something fresh.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🖥️ Demo Screenshot
&lt;/h2&gt;

&lt;p&gt;You can try my &lt;a href="https://mrzaizai2k.github.io/typing" rel="noopener noreferrer"&gt;Simple Typing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6elsjy1uikcmd1xo0j9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6elsjy1uikcmd1xo0j9.png" alt=" " width="800" height="665"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also visit my repo here 👉 &lt;a href="https://github.com/mrzaizai2k/simple-typing/" rel="noopener noreferrer"&gt;mrzaizai2k/simple-typing&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;Just download the &lt;code&gt;typing.html&lt;/code&gt; file, open it in any browser — and you’re ready to practice 🚀.&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 Why I Like It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Minimalistic: no dependencies, just one &lt;code&gt;.html&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Real-time feedback keeps me aware of typing mistakes instantly.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;news fetch feature&lt;/strong&gt; means I can always practice with new content, instead of boring lorem ipsum.&lt;/li&gt;
&lt;li&gt;Great for improving not just speed, but also &lt;strong&gt;accuracy&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔗 Reference
&lt;/h2&gt;

&lt;p&gt;All the code is live here on my GitHub:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://github.com/mrzaizai2k/simple-typing/" rel="noopener noreferrer"&gt;mrzaizai2k/simple-typing&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Feel free to clone it, star it ⭐, or ask me anything.&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>mrzaizai2k</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>A few weeks ago, I built a Docker image for my personal portfolio project (a React app). Everything worked fine — until I checked the size... 💣 1.5 GB! For a static frontend app?! I realized this could slow down deployment, consume more storage, and mak</title>
      <dc:creator>Mai Chi Bao</dc:creator>
      <pubDate>Sun, 27 Jul 2025 03:34:52 +0000</pubDate>
      <link>https://dev.to/mrzaizai2k/a-few-weeks-ago-i-built-a-docker-image-for-my-personal-portfolio-project-a-react-app-everything-481d</link>
      <guid>https://dev.to/mrzaizai2k/a-few-weeks-ago-i-built-a-docker-image-for-my-personal-portfolio-project-a-react-app-everything-481d</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/mrzaizai2k/from-15gb-to-200mb-how-i-slimmed-down-my-docker-image-like-a-pro-12em" class="crayons-story__hidden-navigation-link"&gt;From 1.5GB to 200MB: How I Slimmed Down My Docker Image Like a Pro 🚀&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/mrzaizai2k" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1976523%2F358f751f-fd95-4b8b-becb-99bdb7b5faa9.jpg" alt="mrzaizai2k profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/mrzaizai2k" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Mai Chi Bao
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Mai Chi Bao
                
              
              &lt;div id="story-author-preview-content-2686123" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/mrzaizai2k" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1976523%2F358f751f-fd95-4b8b-becb-99bdb7b5faa9.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Mai Chi Bao&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/mrzaizai2k/from-15gb-to-200mb-how-i-slimmed-down-my-docker-image-like-a-pro-12em" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 21 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/mrzaizai2k/from-15gb-to-200mb-how-i-slimmed-down-my-docker-image-like-a-pro-12em" id="article-link-2686123"&gt;
          From 1.5GB to 200MB: How I Slimmed Down My Docker Image Like a Pro 🚀
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mrzaizai2k"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mrzaizai2k&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/docker"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;docker&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devops"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devops&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/mrzaizai2k/from-15gb-to-200mb-how-i-slimmed-down-my-docker-image-like-a-pro-12em" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;12&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/mrzaizai2k/from-15gb-to-200mb-how-i-slimmed-down-my-docker-image-like-a-pro-12em#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              1&lt;span class="hidden s:inline"&gt;&amp;nbsp;comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>mrzaizai2k</category>
      <category>webdev</category>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>From 1.5GB to 200MB: How I Slimmed Down My Docker Image Like a Pro 🚀</title>
      <dc:creator>Mai Chi Bao</dc:creator>
      <pubDate>Mon, 21 Jul 2025 12:26:00 +0000</pubDate>
      <link>https://dev.to/mrzaizai2k/from-15gb-to-200mb-how-i-slimmed-down-my-docker-image-like-a-pro-12em</link>
      <guid>https://dev.to/mrzaizai2k/from-15gb-to-200mb-how-i-slimmed-down-my-docker-image-like-a-pro-12em</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A real journey of "less is more" in Docker 🐳&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  📚 Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🎯 The Problem: Bloated Docker Image&lt;/li&gt;
&lt;li&gt;🔍 The Investigation&lt;/li&gt;
&lt;li&gt;
🧠 The Solution Explained

&lt;ul&gt;
&lt;li&gt;1. Add &lt;code&gt;.dockerignore&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;2. Use Multi stage Builds&lt;/li&gt;
&lt;li&gt;3. Choose Lighter Base Images&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;📊 Before vs After: What Changed?&lt;/li&gt;

&lt;li&gt;🎁 Conclusion&lt;/li&gt;

&lt;li&gt;🔗 Reference&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 The Problem: Bloated Docker Image
&lt;/h2&gt;

&lt;p&gt;A few weeks ago, I built a Docker image for my personal portfolio project (a React app). Everything worked fine — until I checked the size...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💣 &lt;strong&gt;1.5 GB! For a static frontend app?!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I realized this could slow down deployment, consume more storage, and make CI/CD pipelines sluggish. I had to fix this.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 The Investigation
&lt;/h2&gt;

&lt;p&gt;When I looked into my Docker setup, I noticed some rookie mistakes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I didn’t have a &lt;code&gt;.dockerignore&lt;/code&gt; file 😅&lt;/li&gt;
&lt;li&gt;I was using only a single-stage build&lt;/li&gt;
&lt;li&gt;The base image was &lt;code&gt;node:18-alpine&lt;/code&gt;, which seemed fine but not optimal for production delivery&lt;/li&gt;
&lt;li&gt;I was copying everything (including &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;.git&lt;/code&gt;, and build files) into the image&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is my previous Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;  &lt;span class="c"&gt;# Use official Node image&lt;/span&gt;
  FROM node:18-alpine
  &lt;span class="c"&gt;# Set working directory&lt;/span&gt;
  WORKDIR /app
  &lt;span class="c"&gt;# Copy dependency files first (for better Docker caching)&lt;/span&gt;
  COPY package.json package-lock.json ./
  # Install dependencies
  RUN npm ci
  &lt;span class="c"&gt;# Copy rest of the application&lt;/span&gt;
  COPY . .
  # Build the React app
  RUN npm run build
  &lt;span class="c"&gt;# Install a lightweight static server&lt;/span&gt;
  RUN npm install -g serve 
  &lt;span class="c"&gt;# Expose port&lt;/span&gt;
  EXPOSE 3000
  &lt;span class="c"&gt;# Serve the static build&lt;/span&gt;
  CMD ["serve", "-s", "build", "-l", "3000"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was time to clean house.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 The Solution Explained
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Add dockerignore
&lt;/h3&gt;

&lt;p&gt;Think of &lt;code&gt;.dockerignore&lt;/code&gt; as &lt;code&gt;.gitignore&lt;/code&gt; for Docker. Without it, Docker copies &lt;em&gt;everything&lt;/em&gt; from your working directory — including huge folders like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;node_modules/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.git/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;build/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;*.md&lt;/code&gt; docs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I added this &lt;code&gt;.dockerignore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  node_modules/
  npm-debug.log
  .git
  .gitignore
  Dockerfile
  docker-compose.yml
  build/
  dist/
  .env
  *.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 &lt;strong&gt;Result&lt;/strong&gt;: Way less junk copied into the image = faster build time + smaller size&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Use Multi stage Builds
&lt;/h3&gt;

&lt;p&gt;Instead of one bloated Dockerfile, I split it into two stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Builder Stage&lt;/strong&gt;: Installs dependencies and runs the build&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production Stage&lt;/strong&gt;: Copies only what’s needed to serve the app
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;  &lt;span class="c"&gt;# Stage 1: Build&lt;/span&gt;
  FROM node:18-slim AS builder
  WORKDIR /app
  COPY package*.json ./
  RUN npm ci --only=production
  COPY . .
  RUN npm run build

  &lt;span class="c"&gt;# Stage 2: Serve&lt;/span&gt;
  FROM node:18-slim
  WORKDIR /app
  COPY --from=builder /app/build ./build
  RUN npm install -g serve
  EXPOSE 3000
  CMD ["serve", "-s", "build", "-l", "3000"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 &lt;strong&gt;Result&lt;/strong&gt;: No dev dependencies or source code in the final image. Just the build output. Lean &amp;amp; clean.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Choose Lighter Base Images
&lt;/h3&gt;

&lt;p&gt;Originally I used &lt;code&gt;node:18-alpine&lt;/code&gt;. It’s small but sometimes causes compatibility issues with native modules.&lt;/p&gt;

&lt;p&gt;I switched to &lt;code&gt;node:18-slim&lt;/code&gt;, which is slightly larger than Alpine but more stable for multi-stage builds and still much smaller than the full Node image.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Result&lt;/strong&gt;: Better balance between size and reliability.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Before vs After: What Changed?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&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;📦 Image Size&lt;/td&gt;
&lt;td&gt;~1.5 GB&lt;/td&gt;
&lt;td&gt;~200 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚙️ Build Strategy&lt;/td&gt;
&lt;td&gt;Single-stage&lt;/td&gt;
&lt;td&gt;Multi-stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📁 Base Image&lt;/td&gt;
&lt;td&gt;&lt;code&gt;node:18-alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;node:18-slim&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🚫 .dockerignore&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;✅ Included to skip unneeded files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔁 Copied Files&lt;/td&gt;
&lt;td&gt;Everything (including &lt;code&gt;node_modules&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Only &lt;code&gt;build/&lt;/code&gt; output copied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🚀 Performance&lt;/td&gt;
&lt;td&gt;Slow build + large deploy&lt;/td&gt;
&lt;td&gt;Faster build + minimal production image&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🎁 Conclusion
&lt;/h2&gt;

&lt;p&gt;If you apply the same strategies, you’ll get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Smaller Docker Images&lt;/strong&gt; → Faster pulls/pushes, lower storage cost&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;Faster Builds&lt;/strong&gt; → Especially in CI/CD pipelines&lt;/li&gt;
&lt;li&gt;🔒 &lt;strong&gt;Better Security&lt;/strong&gt; → No unnecessary source code or secrets inside image&lt;/li&gt;
&lt;li&gt;🌍 &lt;strong&gt;Portable &amp;amp; Lightweight Images&lt;/strong&gt; → Ideal for microservices, serverless, and edge deployments&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔗 Reference
&lt;/h2&gt;

&lt;p&gt;All the code is live here on my GitHub:&lt;br&gt;
👉 &lt;a href="https://github.com/mrzaizai2k/Portfolio" rel="noopener noreferrer"&gt;mrzaizai2k/Portfolio&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to clone it, star it ⭐, or ask me anything.&lt;/p&gt;




&lt;p&gt;Thanks for reading!&lt;br&gt;
If this helped you, share it with a friend who might still be shipping 1GB containers 😄&lt;/p&gt;

</description>
      <category>mrzaizai2k</category>
      <category>webdev</category>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>I'm not a professional web developer — but even so, just by following these 5 simple steps, I managed to hit 95 on PageSpeed 🚀 But hey — don’t just skim this. The last tip could make or break all your efforts. Stick around until the end for the real gam</title>
      <dc:creator>Mai Chi Bao</dc:creator>
      <pubDate>Tue, 15 Jul 2025 08:29:17 +0000</pubDate>
      <link>https://dev.to/mrzaizai2k/im-not-a-professional-web-developer-but-even-so-just-by-following-these-5-simple-steps-i-2ali</link>
      <guid>https://dev.to/mrzaizai2k/im-not-a-professional-web-developer-but-even-so-just-by-following-these-5-simple-steps-i-2ali</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/mrzaizai2k/lets-be-honest-slow-websites-scare-people-away-34d4" class="crayons-story__hidden-navigation-link"&gt;Let’s Be Honest… Slow Websites Scare People Away 😢&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/mrzaizai2k" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1976523%2F358f751f-fd95-4b8b-becb-99bdb7b5faa9.jpg" alt="mrzaizai2k profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/mrzaizai2k" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Mai Chi Bao
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Mai Chi Bao
                
              
              &lt;div id="story-author-preview-content-2662570" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/mrzaizai2k" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1976523%2F358f751f-fd95-4b8b-becb-99bdb7b5faa9.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Mai Chi Bao&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/mrzaizai2k/lets-be-honest-slow-websites-scare-people-away-34d4" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 14 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/mrzaizai2k/lets-be-honest-slow-websites-scare-people-away-34d4" id="article-link-2662570"&gt;
          Let’s Be Honest… Slow Websites Scare People Away 😢
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/react"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;react&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mrzaizai2k"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mrzaizai2k&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/performance"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;performance&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/mrzaizai2k/lets-be-honest-slow-websites-scare-people-away-34d4" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;7&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/mrzaizai2k/lets-be-honest-slow-websites-scare-people-away-34d4#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>react</category>
      <category>mrzaizai2k</category>
      <category>webdev</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
