<?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: Rasika Dangamuwa</title>
    <description>The latest articles on DEV Community by Rasika Dangamuwa (@rasika_dangamuwa_ed1074fe).</description>
    <link>https://dev.to/rasika_dangamuwa_ed1074fe</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%2F4025318%2F0ed5e5b1-1a13-4e6f-9289-fc5142aca273.png</url>
      <title>DEV Community: Rasika Dangamuwa</title>
      <link>https://dev.to/rasika_dangamuwa_ed1074fe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rasika_dangamuwa_ed1074fe"/>
    <language>en</language>
    <item>
      <title>The SPF 10-Lookup Trap: Why Transactional Emails Land in Spam</title>
      <dc:creator>Rasika Dangamuwa</dc:creator>
      <pubDate>Wed, 23 Sep 2026 00:31:37 +0000</pubDate>
      <link>https://dev.to/rasika_dangamuwa_ed1074fe/the-spf-10-lookup-trap-why-transactional-emails-land-in-spam-3j5l</link>
      <guid>https://dev.to/rasika_dangamuwa_ed1074fe/the-spf-10-lookup-trap-why-transactional-emails-land-in-spam-3j5l</guid>
      <description>&lt;p&gt;You wire up Google Workspace for team mail, plug in SendGrid for password resets, add Zendesk for support, and link HubSpot for newsletters. Each onboarding checklist ends with the exact same line: &lt;em&gt;Add this include to your SPF TXT record.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You paste them together, save DNS, and everything appears fine. Then customer complaints roll in: corporate clients with strict mail servers never receive their login emails. When you inspect the bounce headers, you spot the culprit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;550 5.7.23 Resent-Message rejected: SPF PermError: too many DNS-lookups
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your DMARC policy is set to &lt;code&gt;p=reject&lt;/code&gt; or &lt;code&gt;p=quarantine&lt;/code&gt;, an SPF &lt;code&gt;PermError&lt;/code&gt; fails DMARC evaluation immediately. Here is why this happens, how the math works, and how to fix it before delivery tanks.&lt;/p&gt;

&lt;h3&gt;
  
  
  The RFC 7208 10-Lookup Limit
&lt;/h3&gt;

&lt;p&gt;Receiving mail transfer agents (MTAs) evaluate your SPF record against RFC 7208. Section 4.6.4 specifies a hard limit: &lt;strong&gt;an SPF check must not perform more than 10 DNS queries that require resolution.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why 10? Bad actors can weaponize SPF for DNS reflection attacks. If an attacker forges mail from a domain whose SPF references dozens of external zones, receiving servers flood those nameservers with recursive queries just to verify one spoofed message.&lt;/p&gt;

&lt;p&gt;The 10-lookup rule prevents abuse, but it catches developers off guard because &lt;strong&gt;nested lookups count against your domain quota, not the vendor’s.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Which Mechanisms Count?
&lt;/h3&gt;

&lt;p&gt;Not every token in an SPF string incurs a lookup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consumes 1 lookup:&lt;/strong&gt; &lt;code&gt;include&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;mx&lt;/code&gt;, &lt;code&gt;ptr&lt;/code&gt;, &lt;code&gt;exists&lt;/code&gt;, &lt;code&gt;redirect&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero lookups (free):&lt;/strong&gt; &lt;code&gt;ip4&lt;/code&gt;, &lt;code&gt;ip6&lt;/code&gt;, &lt;code&gt;all&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider this typical apex record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v=spf1 include:_spf.google.com include:sendgrid.net include:mail.zendesk.com include:spf.protection.outlook.com -all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On paper, that looks like 4 lookups. In reality:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;include:_spf.google.com&lt;/code&gt; (1) queries &lt;code&gt;_netblocks.google.com&lt;/code&gt; (2), &lt;code&gt;_netblocks2.google.com&lt;/code&gt; (3), and &lt;code&gt;_netblocks3.google.com&lt;/code&gt; (4).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;include:spf.protection.outlook.com&lt;/code&gt; consumes another 1-2 recursive lookups.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;include:mail.zendesk.com&lt;/code&gt; resolves its own SPF tree.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;include:sendgrid.net&lt;/code&gt; resolves SendGrid’s delivery cluster.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By the time the MTA follows the third include, your lookup counter hits 11. Evaluation halts immediately and outputs &lt;code&gt;PermError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you are assembling records across multiple providers or parsing an existing line, you can use the browser-based &lt;a href="https://nutilz.com/spf-record-generator" rel="noopener noreferrer"&gt;SPF Record Generator and Parser&lt;/a&gt; to preview vendor presets, validate CIDR notation, and inspect your total mechanism count before committing DNS changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Void DNS Lookup Trap
&lt;/h3&gt;

&lt;p&gt;RFC 7208 Section 4.6.4 also enforces a &lt;strong&gt;Void Lookup Limit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If an MTA queries a domain in an &lt;code&gt;include:&lt;/code&gt; or &lt;code&gt;a:&lt;/code&gt; mechanism and receives &lt;code&gt;NXDOMAIN&lt;/code&gt; (non-existent domain) or &lt;code&gt;NODATA&lt;/code&gt; (empty response), RFC 7208 caps these at &lt;strong&gt;at most 2&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When an old marketing or transactional provider shuts down an SPF endpoint, that single dead include consumes half your allowance. Two defunct includes trigger an instant &lt;code&gt;PermError&lt;/code&gt;, even if your total lookup count is only 4.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anti-Pattern: Multiple SPF Records
&lt;/h3&gt;

&lt;p&gt;When records grow long, a common reflex is creating a second TXT record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TXT @ "v=spf1 include:_spf.google.com ~all"
TXT @ "v=spf1 include:sendgrid.net -all"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Never do this.&lt;/strong&gt; RFC 7208 Section 3.2 explicitly states a domain MUST NOT have multiple &lt;code&gt;v=spf1&lt;/code&gt; TXT records. If an MTA sees more than one SPF record on the same host, it immediately returns &lt;code&gt;PermError&lt;/code&gt;. All directives must live in a single record.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Fix It
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Subdomain Delegation (Recommended)
&lt;/h4&gt;

&lt;p&gt;Instead of authorizing every third-party service on your root apex (&lt;code&gt;company.com&lt;/code&gt;), delegate dedicated subdomains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Staff email: &lt;code&gt;company.com&lt;/code&gt; -&amp;gt; &lt;code&gt;v=spf1 include:_spf.google.com ~all&lt;/code&gt; (4 lookups)&lt;/li&gt;
&lt;li&gt;Transactional: &lt;code&gt;mail.company.com&lt;/code&gt; -&amp;gt; &lt;code&gt;v=spf1 include:sendgrid.net ~all&lt;/code&gt; (2 lookups)&lt;/li&gt;
&lt;li&gt;Marketing: &lt;code&gt;news.company.com&lt;/code&gt; -&amp;gt; &lt;code&gt;v=spf1 include:servers.mcsv.net ~all&lt;/code&gt; (2 lookups)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isolates reputation, simplifies DMARC alignment, and avoids the 10-lookup ceiling.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. SPF Flattening
&lt;/h4&gt;

&lt;p&gt;If you must send from apex, replace recursive &lt;code&gt;include:&lt;/code&gt; domains with their static IP ranges (&lt;code&gt;ip4:198.51.100.0/24&lt;/code&gt;). Because &lt;code&gt;ip4&lt;/code&gt; and &lt;code&gt;ip6&lt;/code&gt; take zero lookups, you can list many blocks safely. However, you must monitor vendor IP changes and update your records when ranges rotate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary Checklist
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Keep total query depth strictly under 10.&lt;/li&gt;
&lt;li&gt;Prune decommissioned vendor includes immediately to prevent void lookup penalties.&lt;/li&gt;
&lt;li&gt;Consolidate into a single TXT record starting with &lt;code&gt;v=spf1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Delegate dedicated subdomains for automated email streams.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When reviewing your DNS configuration, test syntax with the &lt;a href="https://nutilz.com/spf-record-generator" rel="noopener noreferrer"&gt;Nutilz SPF Record Generator&lt;/a&gt; alongside &lt;code&gt;dig TXT yourdomain.com&lt;/code&gt; to verify your authentication headers resolve cleanly.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why File Hashes Fail to Match: 5 Checksum Traps Every Developer Hits</title>
      <dc:creator>Rasika Dangamuwa</dc:creator>
      <pubDate>Tue, 22 Sep 2026 00:32:31 +0000</pubDate>
      <link>https://dev.to/rasika_dangamuwa_ed1074fe/why-file-hashes-fail-to-match-5-checksum-traps-every-developer-hits-1bma</link>
      <guid>https://dev.to/rasika_dangamuwa_ed1074fe/why-file-hashes-fail-to-match-5-checksum-traps-every-developer-hits-1bma</guid>
      <description>&lt;p&gt;You pull down a compiled binary, container archive, or release tarball from a distribution server. You run &lt;code&gt;sha256sum artifact.tar.gz&lt;/code&gt;, copy the hex string, and compare it against the checksum published in the release notes.&lt;/p&gt;

&lt;p&gt;The strings do not match.&lt;/p&gt;

&lt;p&gt;Before you panic about a compromised mirror or a man-in-the-middle attack, consider the more mundane reality: file hashes are hyper-sensitive to byte-level environmental shifts. A single flipped bit or invisible header field scrambles the entire 256-bit digest due to the avalanche effect.&lt;/p&gt;

&lt;p&gt;Here are the five most common traps that cause checksum mismatches in production environments and how to troubleshoot them.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. CRLF vs. LF Line-Ending Normalization
&lt;/h3&gt;

&lt;p&gt;The most common trap when hashing scripts, configuration files, SQL dumps, or JSON fixtures is carriage return normalization.&lt;/p&gt;

&lt;p&gt;On Linux and macOS, newlines are represented by a single byte: line feed (&lt;code&gt;0x0A&lt;/code&gt;, LF). On Windows, newlines default to two bytes: carriage return plus line feed (&lt;code&gt;0x0D 0x0A&lt;/code&gt;, CRLF).&lt;/p&gt;

&lt;p&gt;If a developer checks out a repository with &lt;code&gt;git config core.autocrlf true&lt;/code&gt;, Git silently converts LF to CRLF in the working tree. Even a tiny configuration file will yield an unrecognizable digest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# File with LF&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"server=prod&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;port=8080&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sha256sum&lt;/span&gt;
&lt;span class="c"&gt;# b4c6df44d65008cf...&lt;/span&gt;

&lt;span class="c"&gt;# File with CRLF&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"server=prod&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;port=8080&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sha256sum&lt;/span&gt;
&lt;span class="c"&gt;# d7a810b427cf63bc...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify whether line endings caused your mismatch, run &lt;code&gt;file &amp;lt;filename&amp;gt;&lt;/code&gt; or inspect byte codes with &lt;code&gt;head -n 2 &amp;lt;filename&amp;gt; | od -c&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. The Invisible UTF-8 Byte Order Mark (BOM)
&lt;/h3&gt;

&lt;p&gt;Certain Windows editors and PowerShell redirection operators (like &lt;code&gt;&amp;gt;&lt;/code&gt; in older PowerShell 5.1 sessions) prepend a 3-byte Byte Order Mark (&lt;code&gt;0xEF 0xBB 0xBF&lt;/code&gt;) to UTF-8 text files.&lt;/p&gt;

&lt;p&gt;To text editors, the file looks identical. To a cryptographic hash function, those three extra bytes sit at index zero:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check the first 8 bytes of the file in hex&lt;/span&gt;
xxd &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; 8 config.json
&lt;span class="c"&gt;# If you see efbbbf at the beginning, a BOM was injected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Strip the BOM using &lt;code&gt;sed -i "1s/^\xef\xbb\xbf//" config.json&lt;/code&gt; before verifying.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Archive Non-Determinism in Tar and Zip Files
&lt;/h3&gt;

&lt;p&gt;If you compress the exact same folder of source code on two different machines, the resulting &lt;code&gt;.tar.gz&lt;/code&gt; or &lt;code&gt;.zip&lt;/code&gt; archives will almost never share the same SHA-256 hash.&lt;/p&gt;

&lt;p&gt;Standard archive tools store filesystem metadata inside the archive headers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File modification timestamps (&lt;code&gt;mtime&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;File ordering in the archive directory table&lt;/li&gt;
&lt;li&gt;User IDs (&lt;code&gt;uid&lt;/code&gt;) and Group IDs (&lt;code&gt;gid&lt;/code&gt;) of the creator&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To achieve reproducible archive hashes, you must normalize metadata during creation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Deterministic tar creation&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;--sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;name &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--mtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2026-01-01 00:00:00Z"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--owner&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;--group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;--numeric-owner&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-czf&lt;/span&gt; release.tar.gz ./src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When comparing release artifacts or debugging integrity mismatches across platforms without installing native CLI packages, a browser-based utility like &lt;a href="https://nutilz.com/file-hash-checker" rel="noopener noreferrer"&gt;Nutilz File Hash Checker&lt;/a&gt; allows you to inspect MD5, SHA-1, SHA-256, and SHA-512 digests side by side using client-side WebCrypto without transmitting raw file contents over the network.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Memory Exhaustion on Large Files
&lt;/h3&gt;

&lt;p&gt;When writing verification scripts in Node.js or Python, developers often buffer entire files into memory:&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;// Anti-pattern: loads the entire multi-gigabyte file into heap&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&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;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crypto&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;crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;large-dataset.iso&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Throws ERR_FS_FILE_TOO_LARGE&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once a file exceeds Node.js buffer limits (2 GB) or system memory quotas, the process crashes or silently truncates data. Always compute digests using chunked streams:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&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;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crypto&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;crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;large-dataset.iso&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. Collision Vulnerabilities in MD5 and SHA-1
&lt;/h3&gt;

&lt;p&gt;While modern distribution pipelines rely on SHA-256, legacy systems and vendor mirrors often still publish MD5 or SHA-1 hashes.&lt;/p&gt;

&lt;p&gt;Both algorithms are broken against deliberate chosen-prefix collision attacks. As demonstrated by the SHAttered research team, two completely different PDF documents or binary payloads can be engineered to yield the exact same SHA-1 digest. &lt;/p&gt;

&lt;p&gt;Never use MD5 or SHA-1 as a security boundary against tampering. Treat them strictly as accidental corruption checks for legacy downloads.&lt;/p&gt;




&lt;h3&gt;
  
  
  Summary Checklist
&lt;/h3&gt;

&lt;p&gt;When a file hash fails to match:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inspect the first 16 bytes for a UTF-8 BOM with &lt;code&gt;xxd -l 16 &amp;lt;file&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Check for CRLF line endings using &lt;code&gt;file &amp;lt;file&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If dealing with archives, verify whether the build pipeline enforces deterministic timestamps.&lt;/li&gt;
&lt;li&gt;Verify checksums across both SHA-256 and legacy algorithms using client-side tools like &lt;a href="https://nutilz.com/file-hash-checker" rel="noopener noreferrer"&gt;Nutilz File Hash Checker&lt;/a&gt; or streaming terminal commands.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Taking two minutes to verify byte boundaries and encoding will save hours of chasing ghost security incidents.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>security</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Free vCard &amp; iCal Generator: Turn a Form Into a File Your Phone Already Knows How to Open</title>
      <dc:creator>Rasika Dangamuwa</dc:creator>
      <pubDate>Mon, 21 Sep 2026 00:01:48 +0000</pubDate>
      <link>https://dev.to/rasika_dangamuwa_ed1074fe/free-vcard-ical-generator-turn-a-form-into-a-file-your-phone-already-knows-how-to-open-330j</link>
      <guid>https://dev.to/rasika_dangamuwa_ed1074fe/free-vcard-ical-generator-turn-a-form-into-a-file-your-phone-already-knows-how-to-open-330j</guid>
      <description>&lt;h1&gt;
  
  
  Free vCard &amp;amp; iCal Generator: Turn a Form Into a File Your Phone Already Knows How to Open
&lt;/h1&gt;

&lt;p&gt;Sharing a contact or an event usually means typing it out in a text message or email and hoping the other person copies it correctly. Two old, boring, extremely reliable file formats solve this better than any app: &lt;strong&gt;vCard (.vcf)&lt;/strong&gt; for contacts and &lt;strong&gt;iCalendar (.ics)&lt;/strong&gt; for events. Almost every phone, email client, and calendar app on Earth already knows how to open them — you just need a way to generate one without installing anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://nutilz.com/vcard-ical-generator" rel="noopener noreferrer"&gt;nutilz.com/vcard-ical-generator&lt;/a&gt; is a free two-in-one tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Contact Card (vCard)&lt;/strong&gt; — fill in a name, company, title, phone numbers, email, website, and address, and it builds a standards-compliant &lt;code&gt;.vcf&lt;/code&gt; file (RFC 6350) that any Contacts app can import with one tap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calendar Invite (iCal)&lt;/strong&gt; — fill in a title, start/end time (or mark it all-day), location, description, and organizer, and it builds a standards-compliant &lt;code&gt;.ics&lt;/code&gt; file (RFC 5545) that Google Calendar, Apple Calendar, and Outlook all understand natively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both formats are generated server-side and streamed straight back as a download — nothing you type is stored.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Pick "Contact Card" or "Calendar Invite" at the top of the tool.&lt;/li&gt;
&lt;li&gt;Fill in the fields. For a vCard, only a first or last name is required. For an iCal event, only a title and start time are required — everything else is optional.&lt;/li&gt;
&lt;li&gt;Click generate. The file downloads immediately as &lt;code&gt;.vcf&lt;/code&gt; or &lt;code&gt;.ics&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Open the downloaded file on your phone or computer — it will offer to add it straight to Contacts or Calendar.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For events, one detail worth knowing: start and end times are captured in your browser's local time zone and converted to UTC automatically, so the event lands at the right time regardless of which calendar app or time zone opens it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's useful
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Email signatures&lt;/strong&gt; — attach a &lt;code&gt;.vcf&lt;/code&gt; so recipients get your contact info as a real contact, not text they have to retype.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event invites in a newsletter or plain email&lt;/strong&gt; — most invite tools require the recipient to have an account on the same platform you used. An &lt;code&gt;.ics&lt;/code&gt; file works with literally any calendar app, no account needed on either end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Digital business cards&lt;/strong&gt; — pair the generated vCard with a QR code (nutilz.com also has a QR generator) so someone can scan it at a conference and get your contact saved instantly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One-off events&lt;/strong&gt; — team standups, webinars, appointment reminders — where spinning up a shared calendar invite through a specific platform is overkill.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it free
&lt;/h2&gt;

&lt;p&gt;No signup required: &lt;a href="https://nutilz.com/vcard-ical-generator" rel="noopener noreferrer"&gt;nutilz.com/vcard-ical-generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Formats like vCard and iCalendar have quietly outlived a decade of "smarter" proprietary alternatives precisely because they're boring and universal — worth having a fast, free way to generate one when you need it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Why Client-Side SVG to PNG Conversion Fails in Production: 5 Canvas Traps</title>
      <dc:creator>Rasika Dangamuwa</dc:creator>
      <pubDate>Sun, 20 Sep 2026 00:31:40 +0000</pubDate>
      <link>https://dev.to/rasika_dangamuwa_ed1074fe/why-client-side-svg-to-png-conversion-fails-in-production-5-canvas-traps-2db3</link>
      <guid>https://dev.to/rasika_dangamuwa_ed1074fe/why-client-side-svg-to-png-conversion-fails-in-production-5-canvas-traps-2db3</guid>
      <description>&lt;p&gt;Converting vector SVGs into raster PNGs in the browser seems simple. The typical HTML5 recipe looks clean: serialize an SVG string, wrap it in a &lt;code&gt;Blob&lt;/code&gt;, create an Object URL, assign it to an &lt;code&gt;Image.src&lt;/code&gt;, and call &lt;code&gt;context.drawImage(img, 0, 0)&lt;/code&gt; on a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element before extracting the PNG with &lt;code&gt;canvas.toBlob()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In development with trivial vectors, this works fine. But in production with real-world SVGs exported from Figma, Illustrator, or Sketch, the pipeline quickly breaks. You encounter blank exports, clipped graphics, blurry text, or outright browser security errors.&lt;/p&gt;

&lt;p&gt;Here are five subtle rasterization traps in modern browsers and how to fix them.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. The Missing XMLNS Namespace Failure
&lt;/h3&gt;

&lt;p&gt;When an SVG element is inlined directly in an HTML5 document, browsers render it without needing an explicit XML namespace. But once you serialize it with &lt;code&gt;XMLSerializer&lt;/code&gt; and construct a &lt;code&gt;new Blob([svgText], { type: "image/svg+xml;charset=utf-8" })&lt;/code&gt; for an &lt;code&gt;HTMLImageElement&lt;/code&gt;, the browser processes it as an isolated XML document.&lt;/p&gt;

&lt;p&gt;If the root tag lacks &lt;code&gt;xmlns="http://www.w3.org/2000/svg"&lt;/code&gt;, the parser treats the document as invalid XML. In Chromium and WebKit, image loading either fails silently or resolves with &lt;code&gt;naturalWidth === 0&lt;/code&gt;, producing a blank 0-byte or transparent PNG.&lt;/p&gt;

&lt;p&gt;Always guarantee the namespace before creating the blob:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ensureSvgNamespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;svgString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;svgString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;xmlns=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;svgString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;svg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;svg xmlns=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;http://www.w3.org/2000/svg&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;svgString&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;h3&gt;
  
  
  2. The &lt;code&gt;viewBox&lt;/code&gt; vs. Explicit Dimensions Trap
&lt;/h3&gt;

&lt;p&gt;Design tools frequently export SVGs with responsive attributes like &lt;code&gt;viewBox="0 0 1200 800"&lt;/code&gt; and &lt;code&gt;width="100%"&lt;/code&gt; rather than static pixel dimensions.&lt;/p&gt;

&lt;p&gt;When an &lt;code&gt;HTMLImageElement&lt;/code&gt; loads an SVG lacking explicit &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt;, the browser cannot resolve intrinsic aspect ratios against an unbounded container:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Firefox defaults intrinsic dimensions to &lt;code&gt;300px × 150px&lt;/code&gt; (the standard CSS replaced element fallback).&lt;/li&gt;
&lt;li&gt;Safari can fail to render the image or clip it into a distorted square.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To fix this, parse &lt;code&gt;viewBox&lt;/code&gt; coordinates if explicit dimensions are absent, calculate the aspect ratio, and inject computed width and height attributes onto the root &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; tag before blob creation.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. The Tainted Canvas Security Trap
&lt;/h3&gt;

&lt;p&gt;Calling &lt;code&gt;canvas.toDataURL()&lt;/code&gt; or &lt;code&gt;canvas.toBlob()&lt;/code&gt; can trigger a severe DOM exception:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SecurityError: Failed to execute "toBlob" on "HTMLCanvasElement": Tainted canvases may not be exported.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browsers strictly isolate SVG rendering in image contexts. If your SVG contains an &lt;code&gt;&amp;lt;image&amp;gt;&lt;/code&gt; element with an external URL, an &lt;code&gt;@import&lt;/code&gt; rule in a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block, or a &lt;code&gt;&amp;lt;foreignObject&amp;gt;&lt;/code&gt; element, the canvas context is immediately marked tainted. Even if the remote server sets &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt;, image-context SVG rasterization does not pass CORS credentials.&lt;/p&gt;

&lt;p&gt;When building the client-side &lt;a href="https://nutilz.com/svg-to-png" rel="noopener noreferrer"&gt;SVG to PNG converter on Nutilz&lt;/a&gt;, solving this required pre-fetching external resources with &lt;code&gt;fetch()&lt;/code&gt;, converting them into Base64 &lt;code&gt;data:&lt;/code&gt; URLs, and inlining them directly into the SVG markup before canvas rendering.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. DPI Scaling and Raster Blurriness
&lt;/h3&gt;

&lt;p&gt;SVGs are resolution-independent, but PNGs are rigid pixel rasters. Drawing a 400×300 SVG onto an HTML canvas with matching buffer dimensions produces blurry edges on Retina and high-DPI displays.&lt;/p&gt;

&lt;p&gt;Users also frequently need 2x, 4x, or custom high-res exports. You must decouple logical dimensions from the physical canvas pixel buffer:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setupCanvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logicalWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logicalHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;logicalWidth&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;logicalHeight&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;scale&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;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;imageSmoothingEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;imageSmoothingQuality&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;high&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ctx&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;This ensures vector paths, curves, and fine borders render crisp when serialized.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Web Font Fallbacks in Isolated Contexts
&lt;/h3&gt;

&lt;p&gt;When an SVG references web fonts (such as Inter, Roboto, or custom icon sets) declared on the parent web page, the canvas rasterizer will not inherit those &lt;code&gt;@font-face&lt;/code&gt; definitions.&lt;/p&gt;

&lt;p&gt;Because SVG image rendering occurs in a sandboxed execution context, external network requests for stylesheets and font files are blocked. The browser silently falls back to system fonts (Arial or Times New Roman), disrupting text kerning, line heights, and layout geometry.&lt;/p&gt;

&lt;p&gt;The only reliable fix is embedding font data directly into the SVG using a Base64-encoded &lt;code&gt;@font-face&lt;/code&gt; declaration within an inline &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block before rasterizing.&lt;/p&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Client-side vector conversion offers speed, eliminates backend infrastructure costs, and keeps user files private. However, treating SVG as a simple image overlooks its nature as an XML document with unique security constraints. By validating namespaces, computing dimensions, inlining remote assets, and scaling buffers, you can achieve reliable, crisp PNG exports.&lt;/p&gt;

&lt;p&gt;If you ever need a quick, private utility to convert vector files at custom resolutions without software installs, check out &lt;a href="https://nutilz.com/svg-to-png" rel="noopener noreferrer"&gt;Nutilz SVG to PNG&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>frontend</category>
    </item>
    <item>
      <title>GIF to Video: Convert Any Animated GIF to MP4 or WebM Free</title>
      <dc:creator>Rasika Dangamuwa</dc:creator>
      <pubDate>Sat, 19 Sep 2026 00:02:21 +0000</pubDate>
      <link>https://dev.to/rasika_dangamuwa_ed1074fe/gif-to-video-convert-any-animated-gif-to-mp4-or-webm-free-4h80</link>
      <guid>https://dev.to/rasika_dangamuwa_ed1074fe/gif-to-video-convert-any-animated-gif-to-mp4-or-webm-free-4h80</guid>
      <description>&lt;h1&gt;
  
  
  GIF to Video: Convert Any Animated GIF to MP4 or WebM for Free
&lt;/h1&gt;

&lt;p&gt;Animated GIFs are everywhere, but they're a genuinely bad file format for anything beyond a quick reaction meme. They're often huge compared to a real video of the same content, they can't have audio, and plenty of places — Instagram Stories, video editors, PowerPoint, most CMS upload fields — simply won't accept a &lt;code&gt;.gif&lt;/code&gt; at all. If you've ever tried to drop a GIF into a slide deck or a short-form video app and gotten rejected, converting it to MP4 or WebM first is the fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://nutilz.com/gif-to-video" rel="noopener noreferrer"&gt;GIF to Video&lt;/a&gt; takes an animated GIF and re-encodes it into either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MP4&lt;/strong&gt; (H.264) — the safest choice for compatibility. Works in virtually every video player, editor, and social platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebM&lt;/strong&gt; (VP8) — a smaller, modern format that's ideal if you're embedding the result on a website and want faster load times.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both outputs preserve the GIF's original animation, frame timing, and dimensions. The conversion happens entirely server-side using ffmpeg, so there's no quality loss from screen-recording a GIF playing back or any other workaround hack.&lt;/p&gt;

&lt;p&gt;One detail worth knowing: GIFs support a form of transparency, but neither MP4 nor WebM handle transparency reliably across every player. So any transparent regions in your GIF get flattened onto a white background in the output — this matches how most video platforms behave anyway, and avoids producing a video file that looks broken in players that don't support alpha channels.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://nutilz.com/gif-to-video" rel="noopener noreferrer"&gt;nutilz.com/gif-to-video&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Drag and drop your GIF file, or click to browse and select one&lt;/li&gt;
&lt;li&gt;Choose your output format — MP4 or WebM&lt;/li&gt;
&lt;li&gt;Click convert and download the result&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's the whole process. No account, no watermark, no upload limits beyond a generous 25 MB cap that covers virtually any GIF you'll run into.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's useful
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smaller files, same animation&lt;/strong&gt; — video codecs like H.264 compress motion far more efficiently than GIF's per-frame palette encoding, so the converted file is often a fraction of the original size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upload compatibility&lt;/strong&gt; — many platforms (Instagram, TikTok, LinkedIn, most CMS media libraries) simply reject &lt;code&gt;.gif&lt;/code&gt; uploads but happily accept MP4.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Editing pipelines&lt;/strong&gt; — video editors like Premiere, DaVinci Resolve, or CapCut work with video formats natively; converting your GIF first means it drops straight into a timeline instead of needing a plugin or import workaround.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web performance&lt;/strong&gt; — if you're embedding an animation on a website, an autoplaying, muted &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag with a WebM/MP4 source loads and decodes far more efficiently in the browser than an animated GIF, especially on mobile.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anyone who works with memes, product demos, screen recordings turned into GIFs, or any kind of short looping animation runs into this conversion need eventually — it's one of those small utility tasks that's annoying to solve with a full video editor just to change a container format.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it free
&lt;/h2&gt;

&lt;p&gt;No signup required: &lt;a href="https://nutilz.com/gif-to-video" rel="noopener noreferrer"&gt;https://nutilz.com/gif-to-video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free tools shouldn't require you to create an account, hand over an email address, or sit through a paywall just to do something this simple. That's the whole idea behind nutilz.com — a growing collection of genuinely useful, no-signup tools for everyday file and text tasks.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Free Online Barcode Scanner: Decode Any Barcode or QR Code from a Photo</title>
      <dc:creator>Rasika Dangamuwa</dc:creator>
      <pubDate>Fri, 18 Sep 2026 00:04:32 +0000</pubDate>
      <link>https://dev.to/rasika_dangamuwa_ed1074fe/free-online-barcode-scanner-decode-any-barcode-or-qr-code-from-a-photo-4ghh</link>
      <guid>https://dev.to/rasika_dangamuwa_ed1074fe/free-online-barcode-scanner-decode-any-barcode-or-qr-code-from-a-photo-4ghh</guid>
      <description>&lt;h1&gt;
  
  
  Free Online Barcode Scanner: Decode Any Barcode or QR Code from a Photo
&lt;/h1&gt;

&lt;p&gt;Ever had a barcode you needed to read but no scanner app handy — just a photo on your phone or a screenshot from a PDF? &lt;a href="https://nutilz.com/barcode-scanner" rel="noopener noreferrer"&gt;Nutilz Barcode Scanner&lt;/a&gt; decodes barcodes and QR codes straight from an uploaded image, right in your browser, with no app install and no signup.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Upload any image containing a barcode — a product photo, a screenshot, a scanned label — and it detects and decodes it instantly. It supports a wide range of formats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;QR codes&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retail codes&lt;/strong&gt;: EAN-13, EAN-8, UPC-A, UPC-E&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code formats&lt;/strong&gt;: CODE 128, CODE 39, CODE 93, Codabar, Interleaved 2 of 5&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GS1 DataBar&lt;/strong&gt; (standard and expanded)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PDF417&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ISBN-10 / ISBN-13&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If an image contains multiple barcodes, it detects and lists all of them in one pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://nutilz.com/barcode-scanner" rel="noopener noreferrer"&gt;nutilz.com/barcode-scanner&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Drag and drop an image (or click to browse) — JPEG, PNG, WebP all work&lt;/li&gt;
&lt;li&gt;Click scan&lt;/li&gt;
&lt;li&gt;The decoded value and barcode type appear immediately, with a one-click copy button&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it — no camera permissions needed, since it works from any image file you already have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's useful
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inventory and cataloging&lt;/strong&gt;: quickly pull product codes from photos when building a spreadsheet, instead of retyping long numbers by hand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checking product authenticity or specs&lt;/strong&gt;: decode a UPC/EAN to cross-reference against a product database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recovering data from old scans or screenshots&lt;/strong&gt;: when the original barcode isn't physically available anymore, a saved image is enough&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developers testing barcode generation&lt;/strong&gt;: verify that a barcode your code generated actually encodes the value you expect, without needing a phone scanner app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Library and book cataloging&lt;/strong&gt;: decode ISBN barcodes from cover photos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since it just reads an uploaded image, it also works well for barcodes that are hard to scan live with a shaky phone camera — a well-lit still photo often decodes more reliably than a live camera feed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it free
&lt;/h2&gt;

&lt;p&gt;No signup, no upload limits beyond a reasonable file size cap: &lt;a href="https://nutilz.com/barcode-scanner" rel="noopener noreferrer"&gt;https://nutilz.com/barcode-scanner&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free, fast, single-purpose tools like this are exactly what the web needs more of — no account walls, no ads blocking the result, just the answer you came for.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
      <category>tools</category>
    </item>
    <item>
      <title>Silence Remover: Automatically Cut Dead Air From Audio or Video, Free</title>
      <dc:creator>Rasika Dangamuwa</dc:creator>
      <pubDate>Thu, 17 Sep 2026 00:01:53 +0000</pubDate>
      <link>https://dev.to/rasika_dangamuwa_ed1074fe/silence-remover-automatically-cut-dead-air-from-audio-or-video-free-295p</link>
      <guid>https://dev.to/rasika_dangamuwa_ed1074fe/silence-remover-automatically-cut-dead-air-from-audio-or-video-free-295p</guid>
      <description>&lt;p&gt;Dead air is the thing that makes a podcast, voiceover, or screen recording feel unpolished — the long pause while you gather your thoughts, the silence after a mistake before you re-record the line, the empty stretch at the start of a take before you actually start talking. Editing all of that out by hand, clip by clip, is one of the most tedious parts of post-production. &lt;a href="https://nutilz.com/silence-remover" rel="noopener noreferrer"&gt;Silence Remover&lt;/a&gt; automates it: upload a file, and it cuts every silent gap out for you, for free, with no software to install.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Silence Remover scans an audio or video file for stretches that fall below a volume threshold, removes them, and stitches what's left back into one continuous file. It supports common audio formats (MP3, WAV, OGG, FLAC, AAC, M4A, WMA, OPUS) and video formats (MP4, MOV, WebM, MKV, AVI, M4V) up to 100 MB.&lt;/p&gt;

&lt;p&gt;For video specifically, both the picture and the audio track are trimmed at the exact same points, so lips stay in sync with the voice — it's not just muting the audio, it's actually removing the dead time from the timeline.&lt;/p&gt;

&lt;p&gt;Two things make it more than a blunt on/off cut:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Silence threshold&lt;/strong&gt; — how quiet a section has to be before it counts as "silence." Turn it down if you only want to cut near-total silence; turn it up if you also want quieter background noise or room tone trimmed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimum gap length&lt;/strong&gt; — how long a quiet stretch has to last before it gets cut. This stops the tool from chopping up natural micro-pauses between words and only targets genuinely dead air.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A small buffer is kept on either side of every cut, so a word that starts or ends right at the edge of a silent gap doesn't get clipped mid-syllable.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://nutilz.com/silence-remover" rel="noopener noreferrer"&gt;nutilz.com/silence-remover&lt;/a&gt; and drop in an audio or video file (or click to browse).&lt;/li&gt;
&lt;li&gt;Adjust the threshold and minimum gap sliders if the defaults don't suit your recording — noisier rooms usually want a higher threshold.&lt;/li&gt;
&lt;li&gt;Click "Remove Silence" and let it process — it runs entirely on the server, so there's nothing to install.&lt;/li&gt;
&lt;li&gt;Preview the result in the browser, then download it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why it's useful
&lt;/h2&gt;

&lt;p&gt;This is a genuine time-saver for anyone who records regularly and doesn't want to pay for (or learn) a full editing suite just to tighten up dead air: podcasters cleaning up raw recordings before publishing, YouTubers and educators trimming screen recordings and tutorials, and anyone doing voiceover work who re-records lines and doesn't want to manually hunt down every gap afterward. It won't replace a real edit for pacing or content, but it removes the single most repetitive part of that process automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it free
&lt;/h2&gt;

&lt;p&gt;No signup, no upload limits beyond the file size cap, nothing to install: &lt;a href="https://nutilz.com/silence-remover" rel="noopener noreferrer"&gt;https://nutilz.com/silence-remover&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free tools that just do the boring, repetitive part of a job well are worth more than another account you have to create — that's the whole idea behind nutilz.com.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
      <category>showdev</category>
    </item>
    <item>
      <title>PDF Page Rotator &amp; Reorder: Fix Sideways Scans and Reshuffle Pages, Free</title>
      <dc:creator>Rasika Dangamuwa</dc:creator>
      <pubDate>Wed, 16 Sep 2026 00:02:01 +0000</pubDate>
      <link>https://dev.to/rasika_dangamuwa_ed1074fe/pdf-page-rotator-reorder-fix-sideways-scans-and-reshuffle-pages-free-4cc7</link>
      <guid>https://dev.to/rasika_dangamuwa_ed1074fe/pdf-page-rotator-reorder-fix-sideways-scans-and-reshuffle-pages-free-4cc7</guid>
      <description>&lt;h1&gt;
  
  
  PDF Page Rotator &amp;amp; Reorder: Fix Sideways Scans and Reshuffle Pages, Free
&lt;/h1&gt;

&lt;p&gt;If you've ever scanned a document and gotten a few pages upside down, or merged two PDFs and ended up with the pages in the wrong order, you know how annoying it is to fix without proper software. Most people either live with the mess or install a full PDF editor just to rotate a handful of pages. There's a faster way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nutilz.com/pdf-rotate-reorder" rel="noopener noreferrer"&gt;PDF Page Rotator &amp;amp; Reorder&lt;/a&gt; is a free browser tool that does exactly what the name says: rotate any page in a PDF and drag pages into a new order, then download a single corrected file. No account, no install, no watermark.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Per-page rotation&lt;/strong&gt; — click the rotate button on any page thumbnail to turn it 90° at a time. Each page rotates independently, so you can fix one sideways scan without touching the rest of the document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drag-and-drop reordering&lt;/strong&gt; — drag a page thumbnail to a new spot in the sequence, or use the arrow buttons if you'd rather not drag. Rotation and reordering are fully independent, so you can do either, both, or neither on any given page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live thumbnail preview&lt;/strong&gt; — every page renders as an image as soon as you upload, so you can see exactly what you're rearranging instead of guessing from page numbers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lossless output&lt;/strong&gt; — pages are copied directly from the original PDF's page data. Text stays selectable, images stay full resolution, nothing gets rasterized or recompressed in the process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://nutilz.com/pdf-rotate-reorder" rel="noopener noreferrer"&gt;nutilz.com/pdf-rotate-reorder&lt;/a&gt; and drop in your PDF (up to 50MB, 200 pages).&lt;/li&gt;
&lt;li&gt;Wait for the thumbnails to load — each page shows up as its own tile.&lt;/li&gt;
&lt;li&gt;Click ↻ on any page to rotate it 90° clockwise (click again for 180°, 270°, back to 0°).&lt;/li&gt;
&lt;li&gt;Drag pages into the order you want, or use the ← / → buttons on each tile.&lt;/li&gt;
&lt;li&gt;Download the corrected PDF. It comes out as a single file with your new page order and rotations baked in.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why it's useful
&lt;/h2&gt;

&lt;p&gt;This tool exists for the specific moment when a document is &lt;em&gt;almost&lt;/em&gt; right. A few common cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A multi-page scan where the feeder grabbed one sheet sideways, so every other page reads fine except that one.&lt;/li&gt;
&lt;li&gt;Two PDFs merged in the wrong sequence — say, an appendix that landed before the main report.&lt;/li&gt;
&lt;li&gt;A signed contract scanned upside-down because it went through the scanner face-down.&lt;/li&gt;
&lt;li&gt;A batch of receipts or forms scanned out of order that need to match a specific filing sequence before you archive them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these need a full PDF editor. They need rotation and reordering, which is exactly what this tool is scoped to — nothing more to learn, nothing else in the UI to get in the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it free
&lt;/h2&gt;

&lt;p&gt;No signup required: &lt;a href="https://nutilz.com/pdf-rotate-reorder" rel="noopener noreferrer"&gt;https://nutilz.com/pdf-rotate-reorder&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free tools that solve one specific, annoying problem well tend to be more useful day-to-day than bloated suites that solve everything poorly. This is one of those — give it a try next time a scan comes out crooked.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>tools</category>
      <category>pdf</category>
    </item>
    <item>
      <title>The 00:00 UTC Thundering Herd: Why Cron Schedule Collisions Crash Production Databases</title>
      <dc:creator>Rasika Dangamuwa</dc:creator>
      <pubDate>Tue, 15 Sep 2026 00:31:38 +0000</pubDate>
      <link>https://dev.to/rasika_dangamuwa_ed1074fe/the-0000-utc-thundering-herd-why-cron-schedule-collisions-crash-production-databases-jjk</link>
      <guid>https://dev.to/rasika_dangamuwa_ed1074fe/the-0000-utc-thundering-herd-why-cron-schedule-collisions-crash-production-databases-jjk</guid>
      <description>&lt;p&gt;Every DevOps and backend team has experienced the ghost in the machine: at exactly 00:00:00 UTC, database connections spike, API latencies shoot through the ceiling, and alert channels light up with 504 Gateway Timeouts. Ten minutes later, without human intervention, everything returns to normal.&lt;/p&gt;

&lt;p&gt;The culprit is almost always scheduled task collision—the midnight thundering herd.&lt;/p&gt;

&lt;p&gt;When engineers add background tasks, "run once a day at midnight" is the default instinct. Over months and years, different microservices and scripts pile up on the same schedule. At 00:00, your database is simultaneously hit by database vacuuming, stale session purges, billing reconciliation, Elasticsearch re-indexing, and automated report generation. &lt;/p&gt;

&lt;p&gt;PostgreSQL maxes out its connection pool, IOPS throttles on AWS EBS volumes, and incoming user web traffic stalls waiting for database locks.&lt;/p&gt;

&lt;p&gt;Beyond simple midnight collisions, several subtle cron syntax behaviors and architectural oversights cause production outages. Here is how scheduled jobs break in production and how to prevent cascading failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Infamous POSIX DOM vs. DOW Trap
&lt;/h3&gt;

&lt;p&gt;The single most dangerous edge case in standard Unix/POSIX cron is how Day-of-Month (DOM) and Day-of-Week (DOW) interact.&lt;/p&gt;

&lt;p&gt;In almost every programming language, filtering criteria are combined with logical AND. You might assume that setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 3 1-7 * 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;means "run at 03:00 on the first Monday of the month" (if the day of month is between 1 and 7 AND the day of the week is Monday).&lt;/p&gt;

&lt;p&gt;In POSIX and Vixie cron, this assumption is flatly wrong. Under standard cron specifications, if both the day-of-month and day-of-week fields are specified (meaning neither is an asterisk &lt;code&gt;*&lt;/code&gt;), the two conditions are combined with &lt;strong&gt;OR&lt;/strong&gt;, not &lt;strong&gt;AND&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of running only when Monday falls between the 1st and the 7th, this expression triggers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every single day from the 1st to the 7th of the month, PLUS&lt;/li&gt;
&lt;li&gt;Every Monday throughout the entire month.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A monthly billing job intended to run once per month will fire 11 or 12 times. If the task is not strictly idempotent, users get duplicate charges or multiple billing notifications.&lt;/p&gt;

&lt;p&gt;To achieve true "first Monday of the month" execution in standard cron, you must evaluate the day inside the command itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0 3 1-7 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="se"&gt;\%&lt;/span&gt;u&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 1 &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /usr/local/bin/monthly-sync.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. The 15-Minute Harmonic Resonance
&lt;/h3&gt;

&lt;p&gt;Another common collision pattern is harmonic scheduling across independent microservices.&lt;/p&gt;

&lt;p&gt;Engineers routinely schedule routine syncs at &lt;code&gt;*/15 * * * *&lt;/code&gt; or &lt;code&gt;*/30 * * * *&lt;/code&gt;. When five separate services use identical intervals, they all wake up at :00, :15, :30, and :45. Even if their individual resource footprints are modest, their combined resource spike saturates CPU cores and network interfaces simultaneously.&lt;/p&gt;

&lt;p&gt;The fix is simple: stagger your schedules. Rather than defaulting to minute 0 or round 15-minute marks, assign arbitrary offset minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Service A: runs every 15 mins at :03, :18, :33, :48
3,18,33,48 * * * * /app/sync-inventory

# Service B: runs every 15 mins at :07, :22, :37, :52
7,22,37,52 * * * * /app/refresh-rates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plotting your schedules across a multi-day timeline using a browser tool like &lt;a href="https://nutilz.com/cron-timeline" rel="noopener noreferrer"&gt;nutilz.com/cron-timeline&lt;/a&gt; makes it easy to visualize firing heatmaps and spot these overlapping peaks across your scheduled fleet before deploying them.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Missing Dates and Daylight Saving Time Skips
&lt;/h3&gt;

&lt;p&gt;Two temporal traps reliably cause cron jobs to fail to execute:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Short Months&lt;/strong&gt;: A cron expression like &lt;code&gt;0 4 31 * *&lt;/code&gt; will never execute in February, April, June, September, or November. If an invoice script relies on the 31st, it silently skips 5 months out of the year.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DST Spring Forward&lt;/strong&gt;: In timezones observing Daylight Saving Time, clocks skip from 02:00 to 03:00 in spring. A job scheduled for &lt;code&gt;30 2 * * *&lt;/code&gt; will either be skipped entirely or behave unpredictably depending on whether your cron daemon runs in local time or UTC. Always configure production servers and cron engines in UTC (&lt;code&gt;Etc/UTC&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4. Production Hardening: Locks and Jitter
&lt;/h3&gt;

&lt;p&gt;To prevent scheduled tasks from degrading production stability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Implement Distributed Locking&lt;/strong&gt;: Ensure tasks cannot run concurrently if an earlier run takes longer than expected. Use Redis keys with timeouts (&lt;code&gt;SET key token NX EX 3600&lt;/code&gt;) or PostgreSQL advisory locks (&lt;code&gt;SELECT pg_try_advisory_lock(12345)&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Execution Jitter&lt;/strong&gt;: Add random delay before executing batch requests to external APIs:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;RANDOM &lt;span class="se"&gt;\%&lt;/span&gt; &lt;span class="m"&gt;180&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /usr/local/bin/sync-job
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Audit Timelines Visually&lt;/strong&gt;: Never deploy complex cron expressions to production without inspecting their actual future execution timestamps. Visual timeline tools like the &lt;a href="https://nutilz.com/cron-timeline" rel="noopener noreferrer"&gt;Nutilz Cron Timeline Visualizer&lt;/a&gt; allow you to inspect multi-day execution distribution and verify that your jobs never pile up at the same minute.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>devops</category>
      <category>programming</category>
      <category>database</category>
    </item>
    <item>
      <title>Audio Trimmer: Cut Any Audio Clip Free, Right in Your Browser</title>
      <dc:creator>Rasika Dangamuwa</dc:creator>
      <pubDate>Mon, 14 Sep 2026 00:01:23 +0000</pubDate>
      <link>https://dev.to/rasika_dangamuwa_ed1074fe/audio-trimmer-cut-any-audio-clip-free-right-in-your-browser-3476</link>
      <guid>https://dev.to/rasika_dangamuwa_ed1074fe/audio-trimmer-cut-any-audio-clip-free-right-in-your-browser-3476</guid>
      <description>&lt;h1&gt;
  
  
  Audio Trimmer: Cut Any Audio Clip Free, Right in Your Browser
&lt;/h1&gt;

&lt;p&gt;Got a podcast recording with ten seconds of dead air at the start, a voice memo you only need the middle third of, or a song you want to clip down to a 30-second sample? You don't need Audacity, a DAW, or an account anywhere — you need to drag two handles and hit trim.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://nutilz.com/audio-trimmer" rel="noopener noreferrer"&gt;Audio Trimmer&lt;/a&gt; lets you cut a precise section out of an audio file directly in your browser. Drop in an MP3, WAV, OGG, FLAC, AAC, M4A, WMA, or Opus file up to 100MB, and you get a waveform-style player with two draggable handles for the start and end points. Drag them to where you want the clip to begin and end, preview it, and export just that section — nothing else touches the file.&lt;/p&gt;

&lt;p&gt;Clips can run up to 30 minutes, which covers almost anything short of a full album or a long-form podcast episode. The output keeps the original quality; there's no re-encoding pass that mushes your audio into a lower bitrate to save processing time.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://nutilz.com/audio-trimmer" rel="noopener noreferrer"&gt;nutilz.com/audio-trimmer&lt;/a&gt; and drop your audio file onto the page (or click to browse).&lt;/li&gt;
&lt;li&gt;Once it loads, you'll see the total duration and two handles on the timeline — one for the start, one for the end.&lt;/li&gt;
&lt;li&gt;Drag each handle to the point you want to keep. The current start/end times update live as you move them.&lt;/li&gt;
&lt;li&gt;Hit play to preview just the trimmed section before you commit.&lt;/li&gt;
&lt;li&gt;Click trim/export, and the cut file downloads straight to your device.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's the whole workflow. No upload-then-wait-in-a-queue step, no email confirmation, no watermark stamped on your output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's useful
&lt;/h2&gt;

&lt;p&gt;Most audio editing is overkill for a trim. If all you need is to remove silence from the front of a recording, isolate a quote from an interview, grab a ringtone-length clip, or shorten a voice note before sending it, opening a full DAW is a lot of friction for a ten-second job. A browser-based trimmer that does exactly one thing well is faster for that case every time.&lt;/p&gt;

&lt;p&gt;It's also handy alongside nutilz's other audio tools — trim a clip here, then merge it with another using &lt;a href="https://nutilz.com/audio-merge" rel="noopener noreferrer"&gt;Audio Merge&lt;/a&gt; if you're stitching pieces together, or run it through &lt;a href="https://nutilz.com/silence-remover" rel="noopener noreferrer"&gt;Silence Remover&lt;/a&gt; first if the raw recording has dead air scattered throughout rather than just at the edges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it free
&lt;/h2&gt;

&lt;p&gt;No signup, no install, no watermark: &lt;a href="https://nutilz.com/audio-trimmer" rel="noopener noreferrer"&gt;https://nutilz.com/audio-trimmer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free tools that just do the one job you actually came for, without asking for your email first, are worth keeping around.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>javascript</category>
      <category>tools</category>
    </item>
    <item>
      <title>How to Merge Multiple Audio Clips Into One File (Free, No Software)</title>
      <dc:creator>Rasika Dangamuwa</dc:creator>
      <pubDate>Sun, 13 Sep 2026 00:02:19 +0000</pubDate>
      <link>https://dev.to/rasika_dangamuwa_ed1074fe/how-to-merge-multiple-audio-clips-into-one-file-free-no-software-25fp</link>
      <guid>https://dev.to/rasika_dangamuwa_ed1074fe/how-to-merge-multiple-audio-clips-into-one-file-free-no-software-25fp</guid>
      <description>&lt;p&gt;Anyone who's tried to combine a handful of voice memos, podcast segments, or song clips into one file knows the usual options aren't great: install a desktop editor you'll use once, wrestle with a mobile app that adds a watermark, or hunt down an ffmpeg command and hope you remember the syntax. Most of the time you just want the files stitched together in order, as one clean track, without installing anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core problem: mismatched formats
&lt;/h2&gt;

&lt;p&gt;The annoying part isn't joining audio — it's that the clips you want to combine are rarely in the same format. A voice memo from a phone might be an M4A at 44.1kHz mono, a podcast segment might be a stereo WAV at 48kHz, and a background track might be an MP3 at some other rate entirely. Naively concatenating files with mismatched sample rates or channel layouts produces glitches, speed changes, or outright silence in some players — the raw bytes don't line up.&lt;/p&gt;

&lt;p&gt;The fix is to decode everything back to raw PCM audio, resample and re-channel it to a single common format, and then re-encode the whole thing as one file. That's more than a simple "paste these files together" operation, which is why it usually needs actual audio tooling rather than a file-concatenation trick.&lt;/p&gt;

&lt;h2&gt;
  
  
  A free tool that does this in the browser
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://nutilz.com/audio-merge" rel="noopener noreferrer"&gt;Audio Merger&lt;/a&gt; handles exactly this. You drop in your clips — MP3, WAV, OGG, FLAC, AAC, M4A, WMA, or Opus, and you can mix formats in the same batch — reorder them with simple up/down controls, and it normalizes every clip to a matching sample rate and channel layout server-side before joining them into a single MP3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accepts up to 8 clips per job, each up to 40 MB, 150 MB combined&lt;/li&gt;
&lt;li&gt;Automatically normalizes differing sample rates and channel layouts (mono/stereo) so mixed-format clips merge cleanly instead of glitching&lt;/li&gt;
&lt;li&gt;Lets you reorder clips before merging, so you control the final sequence&lt;/li&gt;
&lt;li&gt;Always outputs a single standard MP3, playable anywhere&lt;/li&gt;
&lt;li&gt;Runs entirely server-side — no plugins, no desktop software, and files are deleted immediately after processing rather than stored&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to use it:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Drag and drop two or more audio files onto the upload area (or click to browse).&lt;/li&gt;
&lt;li&gt;Use the up/down arrows to put the clips in the order you want them to play.&lt;/li&gt;
&lt;li&gt;Click "Merge Clips" and wait for the server to process them.&lt;/li&gt;
&lt;li&gt;Preview the result in the built-in player, then download the merged MP3.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Where this is actually useful
&lt;/h2&gt;

&lt;p&gt;A few real situations where this comes up: stitching together several short voice memos into one recording instead of sending someone five separate files, assembling podcast segments (intro, interview, outro) recorded at different times into a single episode file, combining short song clips into a mixtape-style track, or joining audio notes recorded across multiple takes into one continuous file for transcription.&lt;/p&gt;

&lt;p&gt;It's a narrow tool by design — it merges, it doesn't trim, mix volumes, or add effects. If you need to cut a clip down first, a companion &lt;a href="https://nutilz.com/audio-trimmer" rel="noopener noreferrer"&gt;Audio Trimmer&lt;/a&gt; handles that, and an &lt;a href="https://nutilz.com/audio-converter" rel="noopener noreferrer"&gt;Audio Format Converter&lt;/a&gt; is there if you just need to change formats without merging anything.&lt;/p&gt;

&lt;p&gt;Try it here, free, no signup: &lt;a href="https://nutilz.com/audio-merge" rel="noopener noreferrer"&gt;https://nutilz.com/audio-merge&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Small, single-purpose tools like this are underrated — you don't always need a full DAW to solve a five-minute problem, you just need the one operation you're actually trying to do, done correctly.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Subtitle Burner: Free Tool to Hardcode SRT/VTT Captions Into Any Video</title>
      <dc:creator>Rasika Dangamuwa</dc:creator>
      <pubDate>Sat, 12 Sep 2026 00:01:54 +0000</pubDate>
      <link>https://dev.to/rasika_dangamuwa_ed1074fe/subtitle-burner-free-tool-to-hardcode-srtvtt-captions-into-any-video-2nm0</link>
      <guid>https://dev.to/rasika_dangamuwa_ed1074fe/subtitle-burner-free-tool-to-hardcode-srtvtt-captions-into-any-video-2nm0</guid>
      <description>&lt;h1&gt;
  
  
  Subtitle Burner: Hardcode SRT/VTT Captions Into Any Video for Free
&lt;/h1&gt;

&lt;p&gt;If you've ever needed to add captions to a video for social media, a tutorial, or accessibility, you know the usual options are a pain: video editors with steep learning curves, desktop software that eats gigabytes of disk space, or paid SaaS tools gated behind a subscription. Subtitle Burner skips all of that — it's a free, browser-based tool that permanently burns SRT or VTT subtitles into your video file.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Subtitle Burner takes two inputs — a video file (MP4, MOV, WebM, MKV, or AVI, up to 100 MB) and a subtitle file (.srt or .vtt, up to 2 MB) — and renders the captions directly into the video frames. Unlike subtitle tracks that a player can toggle on or off, "burned-in" captions are baked into the picture itself, so they show up everywhere: Instagram, TikTok, WhatsApp shares, embedded players, anywhere that doesn't respect separate subtitle tracks.&lt;/p&gt;

&lt;p&gt;You can pick from three font sizes (small, medium, large) to match the video's resolution and viewing context, then download the finished file once processing completes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://nutilz.com/subtitle-burner" rel="noopener noreferrer"&gt;nutilz.com/subtitle-burner&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Drop in your video file, or click to browse&lt;/li&gt;
&lt;li&gt;Drop in your .srt or .vtt subtitle file&lt;/li&gt;
&lt;li&gt;Choose a font size&lt;/li&gt;
&lt;li&gt;Click burn, then download the result once it's ready&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No account, no upload limits beyond the file size caps, and no watermark on the output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's useful
&lt;/h2&gt;

&lt;p&gt;Burned-in subtitles solve a real distribution problem: most short-form video platforms autoplay muted, and viewers scroll past anything they have to tap to understand. Captions baked into the frame get watched. They're also useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repurposing long-form content (podcasts, webinars, lectures) into clip-friendly captioned segments&lt;/li&gt;
&lt;li&gt;Making tutorial videos accessible without relying on a platform's auto-caption feature&lt;/li&gt;
&lt;li&gt;Sharing videos in contexts where the player won't render a separate subtitle track (raw file shares, some embeds, older devices)&lt;/li&gt;
&lt;li&gt;Language learners who want captions permanently visible regardless of player settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you already have a transcript or auto-generated captions in SRT/VTT format — from YouTube, Whisper, or a captioning service — this tool is the fastest way to turn them into a shareable, caption-baked video without installing anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it free
&lt;/h2&gt;

&lt;p&gt;No signup required: &lt;a href="https://nutilz.com/subtitle-burner" rel="noopener noreferrer"&gt;https://nutilz.com/subtitle-burner&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free tools shouldn't come with friction. Subtitle Burner runs the conversion, hands you the file, and gets out of your way.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>video</category>
      <category>tools</category>
    </item>
  </channel>
</rss>
