<?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: FindUtils</title>
    <description>The latest articles on DEV Community by FindUtils (@findutils).</description>
    <link>https://dev.to/findutils</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%2F4181270%2F97019dc9-d51e-4b73-8b3f-897638a36f69.png</url>
      <title>DEV Community: FindUtils</title>
      <link>https://dev.to/findutils</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/findutils"/>
    <language>en</language>
    <item>
      <title>Cookie-Free Analytics Explained: How Privacy-Friendly Analytics Count Visitors Without Cookies</title>
      <dc:creator>FindUtils</dc:creator>
      <pubDate>Wed, 14 Oct 2026 12:34:54 +0000</pubDate>
      <link>https://dev.to/findutils/cookie-free-analytics-explained-how-privacy-friendly-analytics-count-visitors-without-cookies-4fip</link>
      <guid>https://dev.to/findutils/cookie-free-analytics-explained-how-privacy-friendly-analytics-count-visitors-without-cookies-4fip</guid>
      <description>&lt;p&gt;For years, “counting visitors” meant dropping a cookie (or three), stitching sessions across days, and hoping the consent banner was enough. A growing set of products take a different path: measure traffic with &lt;strong&gt;no cookie on the visitor by default&lt;/strong&gt;, keep reports at the aggregate level, and treat person-level profiles as an explicit opt-in — not the starting point.&lt;/p&gt;

&lt;p&gt;This piece has two layers. First, the &lt;strong&gt;general technique&lt;/strong&gt; behind cookieless / privacy-friendly analytics (industry knowledge — not unique to any one vendor). Second, what &lt;a href="https://datavisitors.com/" rel="noopener noreferrer"&gt;DataVisitors&lt;/a&gt; documents about its own approach on its privacy, how-it-works, and product pages. Those two layers stay separate on purpose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; DataVisitors is &lt;strong&gt;waitlist-only&lt;/strong&gt; right now. Accounts open by invitation. There is no “start a free trial” or “create an account now” path on the public site — join the &lt;a href="https://datavisitors.com/waitlist" rel="noopener noreferrer"&gt;waitlist&lt;/a&gt;, confirm the email, and wait for a seat. You can explore the &lt;a href="https://datavisitors.com/demo" rel="noopener noreferrer"&gt;live demo&lt;/a&gt; without an account.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Nothing in this article is legal advice. Cookie banners, consent, and ePrivacy rules depend on your jurisdiction, your other trackers, and how you configure the product. Talk to counsel for compliance decisions.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why cookies became the default (and why people want out)
&lt;/h2&gt;

&lt;p&gt;Classic web analytics often:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sets a first-party cookie with a stable visitor ID.&lt;/li&gt;
&lt;li&gt;Reuses that ID across days and sometimes across subdomains.&lt;/li&gt;
&lt;li&gt;Builds funnels, retention, and “who is this person?” views from that ID.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That model is powerful. It is also exactly what many privacy laws and browser features push against: persistent identifiers on the device, cross-context tracking, and opaque third-party beacons. Cookieless analytics try to answer a narrower question well — &lt;em&gt;how many visits, from where, to which pages, completing which goals&lt;/em&gt; — without needing a durable ID in the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  The general technique: daily salted hashes (illustrative)
&lt;/h2&gt;

&lt;p&gt;A common cookieless pattern looks like this (simplified; &lt;strong&gt;illustrative&lt;/strong&gt;, not a claim that any specific product uses this exact code):&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;// ILLUSTRATIVE ONLY — not DataVisitors source code&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node: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;function&lt;/span&gt; &lt;span class="nf"&gt;dailyVisitorKey&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;ip&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="nx"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;utcDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dailySalt&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Salt rotates once per UTC day; projectId keeps sites from sharing IDs&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;material&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;utcDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dailySalt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ip&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="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;|&lt;/span&gt;&lt;span class="dl"&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;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="s1"&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;material&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="s1"&gt;hex&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="c1"&gt;// Count uniques inside one UTC day by inserting the key into a set/HLL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this buys you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Same person, same day, same site&lt;/strong&gt; → same key → counts once toward daily uniques.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next UTC day&lt;/strong&gt; → new salt → new key → you &lt;strong&gt;cannot&lt;/strong&gt; stitch a continuous multi-day journey from the identifier alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same person on two projects&lt;/strong&gt; → different &lt;code&gt;projectId&lt;/code&gt; in the hash → two numbers, not one cross-site profile.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trade-offs (general industry knowledge):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You gain&lt;/th&gt;
&lt;th&gt;You usually lose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No cookie required for basic counting&lt;/td&gt;
&lt;td&gt;Reliable cross-day “unique visitors” as one person&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Harder to build a stalking-style profile from the analytics ID&lt;/td&gt;
&lt;td&gt;Some retention / returning-user charts unless another consented ID exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Smaller consent surface for &lt;em&gt;analytics-only&lt;/em&gt; setups (still context-dependent)&lt;/td&gt;
&lt;td&gt;Perfect bot resistance from the ID alone&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A rotating identifier is &lt;strong&gt;not&lt;/strong&gt; a guarantee that all data is anonymous. IP addresses, referrers, and rare page paths can still be identifying in combination. Privacy-friendly products typically also drop or coarsen fields (e.g. country instead of precise location) and keep public share links at aggregate totals only.&lt;/p&gt;

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

&lt;p&gt;The following is taken from DataVisitors’ own pages — &lt;a href="https://datavisitors.com/privacy-approach" rel="noopener noreferrer"&gt;privacy approach&lt;/a&gt;, &lt;a href="https://datavisitors.com/how-it-works" rel="noopener noreferrer"&gt;how it works&lt;/a&gt;, &lt;a href="https://datavisitors.com/faq" rel="noopener noreferrer"&gt;FAQ&lt;/a&gt;, &lt;a href="https://datavisitors.com/features/web-analytics" rel="noopener noreferrer"&gt;web analytics&lt;/a&gt;, and integrations.&lt;/p&gt;

&lt;h3&gt;
  
  
  No cookie on a visitor (by default)
&lt;/h3&gt;

&lt;p&gt;The privacy page states: &lt;strong&gt;“The tracking script sets no cookie. It starts stateless.”&lt;/strong&gt; The FAQ repeats that the tracker starts stateless and that whether device storage is ever used depends on the project’s &lt;strong&gt;privacy regime&lt;/strong&gt; and the visitor’s consent.&lt;/p&gt;

&lt;h3&gt;
  
  
  A daily, project-scoped identifier
&lt;/h3&gt;

&lt;p&gt;Privacy approach: a visit is counted with &lt;strong&gt;“the day’s salt, minted once per UTC day.”&lt;/strong&gt; The stored sample record describes the visitor as a &lt;strong&gt;“daily-salted hash, project-scoped.”&lt;/strong&gt; Explicitly: &lt;strong&gt;“One person on two sites is two numbers”&lt;/strong&gt; because the project is inside the hash — visitors cannot be joined across sites via that identifier.&lt;/p&gt;

&lt;p&gt;FAQ phrasing: &lt;em&gt;“Stateless measurement uses a daily identifier that differs between projects. Device storage depends on the project's privacy mode and the visitor's consent. A rotating identifier does not guarantee that all data is anonymous.”&lt;/em&gt; That last sentence is worth quoting in full — it matches the honest industry caveat above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Privacy regimes you choose per project
&lt;/h3&gt;

&lt;p&gt;DataVisitors documents three regimes (&lt;a href="https://datavisitors.com/privacy-approach" rel="noopener noreferrer"&gt;privacy approach&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automatic (default):&lt;/strong&gt; Stateless where the law is strict; device storage elsewhere when allowed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict:&lt;/strong&gt; Nothing stored on the device anywhere until your site’s consent allows it; until then every visit uses the daily salt only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relaxed (opt-out):&lt;/strong&gt; A first-party id may be kept on the device by default; Global Privacy Control, Do Not Track, and an opt-out call are honoured.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The page also says Global Privacy Control and Do Not Track are respected in all regimes; only an explicit consent from your own banner outranks them. In the EEA, UK, Switzerland, or an unresolved country, nothing is stored on the device until your site’s consent allows it (Automatic / Strict behaviour as documented).&lt;/p&gt;

&lt;h3&gt;
  
  
  Aggregates first; People off by default
&lt;/h3&gt;

&lt;p&gt;Public share links show &lt;strong&gt;aggregates only&lt;/strong&gt;, never an individual visitor. The &lt;strong&gt;People&lt;/strong&gt; feature stays closed until an administrator opens it, and switching it on is recorded. Exports, erasure, and account closure are described as available on every plan — erasure removes a person’s events and traits and keeps a hashed tombstone.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you can measure (web analytics)
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://datavisitors.com/features/web-analytics" rel="noopener noreferrer"&gt;web analytics&lt;/a&gt; feature page describes visitors, page views, sessions, sources (referrer / channel / UTM), pages, engagement (bounce, duration, scroll depth), goals, bot/referrer-spam filtering, and an &lt;strong&gt;AI assistants&lt;/strong&gt; channel for recognized referrers such as ChatGPT, Claude, Perplexity, Gemini, and Copilot. It also clarifies visitor vs session vs page view, and warns that dual-counting page views from both browser and server will inflate totals.&lt;/p&gt;

&lt;h3&gt;
  
  
  How you connect it (when invited)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://datavisitors.com/how-it-works" rel="noopener noreferrer"&gt;How it works&lt;/a&gt; and the &lt;a href="https://datavisitors.com/integrations/browser" rel="noopener noreferrer"&gt;browser integration&lt;/a&gt; document a deferred snippet shaped like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script
  &lt;/span&gt;&lt;span class="na"&gt;defer&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://api.datavisitors.com/assets/sg.js"&lt;/span&gt;
  &lt;span class="na"&gt;data-key=&lt;/span&gt;&lt;span class="s"&gt;"YOUR-INSTALL-KEY"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set allowed origins and privacy settings &lt;strong&gt;before&lt;/strong&gt; the first event. Use one source for page views (browser &lt;em&gt;or&lt;/em&gt; server, not both for the same load). Server-side and webhook events go through the &lt;a href="https://datavisitors.com/integrations/api" rel="noopener noreferrer"&gt;HTTP API&lt;/a&gt; with separate ingest and read keys.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pricing context (no “start now”)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://datavisitors.com/pricing" rel="noopener noreferrer"&gt;Pricing&lt;/a&gt; lists Free / Starter / Growth / Business with event ceilings and feature gates, but prices are marked &lt;strong&gt;“not published yet.”&lt;/strong&gt; Every plan CTA is &lt;strong&gt;Join the waitlist&lt;/strong&gt;. Past an events ceiling, the site says collection continues; the plan gates &lt;em&gt;reading&lt;/em&gt;. That is product documentation, not an invitation to sign up today.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this compares to “just turn off cookies in GA”
&lt;/h2&gt;

&lt;p&gt;Flipping a cookieless flag in a cookie-first product is not the same architecture as designing aggregates + daily salts from day one. Cookie-first tools often still lean on device storage when allowed, and their mental model remains “user ID → journey.” Privacy-first tools push you toward questions the aggregate can answer: which sources convert, which pages bounce, whether a release moved the needle — and they make person-level data a deliberate switch.&lt;/p&gt;

&lt;p&gt;If you also run free browser-only utilities for day-to-day debugging (headers, JSON, PDF chores), that is a separate trust story from analytics — &lt;a href="https://findutils.com/" rel="noopener noreferrer"&gt;FindUtils&lt;/a&gt; keeps those tools client-side. Analytics still needs a server somewhere to store counts; the privacy question is &lt;em&gt;what&lt;/em&gt; you store and &lt;em&gt;how&lt;/em&gt; you identify a visit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical checklist if you are evaluating cookieless analytics
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read the privacy page, not only the homepage.&lt;/strong&gt; Look for: cookies or not, salt rotation period, cross-site joining, consent behaviour, GPC/DNT.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decide your regime before the first event.&lt;/strong&gt; Changing later does not rewrite history the way you might hope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One page-view source.&lt;/strong&gt; Dual instrumentation doubles page views.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Goals over vanity uniques.&lt;/strong&gt; A signup goal survives cookieless limits better than “users this month” marketing slides.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask what public links expose.&lt;/strong&gt; Aggregate-only share URLs are a strong signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For DataVisitors specifically:&lt;/strong&gt; explore the &lt;a href="https://datavisitors.com/demo" rel="noopener noreferrer"&gt;demo&lt;/a&gt;, read &lt;a href="https://datavisitors.com/privacy-approach" rel="noopener noreferrer"&gt;privacy&lt;/a&gt; and &lt;a href="https://datavisitors.com/faq" rel="noopener noreferrer"&gt;FAQ&lt;/a&gt;, then &lt;a href="https://datavisitors.com/waitlist" rel="noopener noreferrer"&gt;join the waitlist&lt;/a&gt;. Do not expect a self-serve signup today.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;Cookie-free analytics usually means: &lt;strong&gt;no cookie by default&lt;/strong&gt;, a &lt;strong&gt;short-lived or salted identifier&lt;/strong&gt; for same-day counting, &lt;strong&gt;project scoping&lt;/strong&gt; so IDs do not federate across sites, and &lt;strong&gt;aggregates as the default report&lt;/strong&gt;. DataVisitors documents exactly that pattern — daily-salted, project-scoped hashes; three privacy regimes; People off until an admin enables it; waitlist-gated accounts.&lt;/p&gt;

&lt;p&gt;Start at &lt;a href="https://datavisitors.com/" rel="noopener noreferrer"&gt;datavisitors.com&lt;/a&gt;, dig into the &lt;a href="https://datavisitors.com/privacy-approach" rel="noopener noreferrer"&gt;privacy approach&lt;/a&gt; and &lt;a href="https://datavisitors.com/how-it-works" rel="noopener noreferrer"&gt;how it works&lt;/a&gt;, try the &lt;a href="https://datavisitors.com/demo" rel="noopener noreferrer"&gt;demo&lt;/a&gt;, and join the &lt;a href="https://datavisitors.com/waitlist" rel="noopener noreferrer"&gt;waitlist&lt;/a&gt; if you want a seat when invitations open.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>privacy</category>
      <category>webdev</category>
      <category>gdpr</category>
    </item>
    <item>
      <title>Add an Emoji Favicon with One Line of HTML (Next.js, Astro, Vue, WordPress) Plus Dynamic Tab Notifications</title>
      <dc:creator>FindUtils</dc:creator>
      <pubDate>Wed, 14 Oct 2026 12:34:11 +0000</pubDate>
      <link>https://dev.to/findutils/add-an-emoji-favicon-with-one-line-of-html-nextjs-astro-vue-wordpress-plus-dynamic-tab-4abl</link>
      <guid>https://dev.to/findutils/add-an-emoji-favicon-with-one-line-of-html-nextjs-astro-vue-wordpress-plus-dynamic-tab-4abl</guid>
      <description>&lt;p&gt;Favicons used to mean a ZIP of PNGs, an ICO, an apple-touch icon, and a half-remembered &lt;code&gt;manifest.json&lt;/code&gt;. For a lot of sites, that is still the right approach. For many others — a side project, a docs site, a staging deploy, a dashboard that needs a notification badge — you only need one emoji in the tab, and you need it &lt;em&gt;today&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://emojifavicons.com/" rel="noopener noreferrer"&gt;EmojiFavicons&lt;/a&gt; is a URL-based emoji favicon service from FindUtils. You point a normal &lt;code&gt;&amp;lt;link rel="icon"&amp;gt;&lt;/code&gt; at an emoji slug, and the API returns an SVG (under 200 bytes for a plain emoji, per the docs). No signup, no API key, no build step. This article sticks to what the site documents and what the endpoints actually return when you request them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-line install
&lt;/h2&gt;

&lt;p&gt;Drop this in your HTML &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://emojifavicons.com/fire"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;fire&lt;/code&gt; with any emoji name. The homepage and &lt;a href="https://emojifavicons.com/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt; state that EmojiFavicons covers &lt;strong&gt;3,773&lt;/strong&gt; Unicode emojis, searchable by &lt;strong&gt;11,550&lt;/strong&gt; keywords. Names and keywords are case-insensitive. You can also use a keyword (&lt;code&gt;/hot&lt;/code&gt; resolves to fire), a URL-encoded emoji character, or a country code for flags (&lt;code&gt;/US&lt;/code&gt;, &lt;code&gt;/TR&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;A live request to &lt;code&gt;https://emojifavicons.com/fire&lt;/code&gt; returns &lt;code&gt;image/svg+xml&lt;/code&gt; — a tiny SVG wrapping the 🔥 glyph. Safari is a special case: the &lt;a href="https://emojifavicons.com/how-it-works" rel="noopener noreferrer"&gt;how-it-works&lt;/a&gt; page explains that Safari has limited SVG favicon support, so the service auto-serves a PNG (Twemoji) for Safari User-Agents. You can force a format with &lt;code&gt;?svg&lt;/code&gt; or &lt;code&gt;?png&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you later want a classic downloadable package instead of a live URL, the docs also expose &lt;code&gt;GET /download/:emoji&lt;/code&gt; — for example &lt;code&gt;https://emojifavicons.com/download/rocket&lt;/code&gt; returns a ZIP of standard sizes. That is the honest self-host path when you do not want a third-party dependency at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customize with URL parameters
&lt;/h2&gt;

&lt;p&gt;Every favicon endpoint accepts query parameters. A documented example that works today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://emojifavicons.com/fire?size=64&amp;amp;bg=1a1a1a&amp;amp;animate=pulse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That request returns &lt;code&gt;image/svg+xml&lt;/code&gt; with a 64×64 viewBox, a dark background, and a CSS &lt;code&gt;pulse&lt;/code&gt; animation. Useful parameters from the docs (not an exhaustive list):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;What it does (per docs)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SVG size in pixels (16–512; default 48)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Background hex without &lt;code&gt;#&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;shape&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;e.g. &lt;code&gt;circle&lt;/code&gt;, &lt;code&gt;rounded&lt;/code&gt;, &lt;code&gt;hexagon&lt;/code&gt;, …&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;animate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;e.g. &lt;code&gt;pulse&lt;/code&gt;, &lt;code&gt;bounce&lt;/code&gt;, &lt;code&gt;spin&lt;/code&gt;, and many others&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;badge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Notification overlay: &lt;code&gt;1&lt;/code&gt;–&lt;code&gt;99&lt;/code&gt; or &lt;code&gt;dot&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;theme&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Branded presets such as &lt;code&gt;github-dark&lt;/code&gt;, &lt;code&gt;vercel-dark&lt;/code&gt;, &lt;code&gt;tailwind&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;png&lt;/code&gt; / &lt;code&gt;svg&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Force output format&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Combine freely. Combos put 2–10 emojis side by side with a &lt;code&gt;+&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://emojifavicons.com/fire+rocket"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That endpoint returns a single SVG with 🔥 and 🚀 next to each other — useful for “shipping fast” vibes without designing an icon.&lt;/p&gt;

&lt;p&gt;For a red unread count without JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://emojifavicons.com/bell?badge=3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Framework installs (verified against the official guides)
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://emojifavicons.com/frameworks" rel="noopener noreferrer"&gt;frameworks index&lt;/a&gt; lists 19 tutorials. Below are the four most common, copied from the site’s own guides. In every case, remove or rename any default &lt;code&gt;favicon.ico&lt;/code&gt; / &lt;code&gt;favicon.svg&lt;/code&gt; in &lt;code&gt;public/&lt;/code&gt; so the external URL wins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next.js (App Router)
&lt;/h3&gt;

&lt;p&gt;From the &lt;a href="https://emojifavicons.com/emoji-favicon-for-nextjs" rel="noopener noreferrer"&gt;Next.js guide&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/layout.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://emojifavicons.com/rocket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;apple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://emojifavicons.com/apple-touch-icon/rocket&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pages Router: put the &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags in &lt;code&gt;pages/_document.tsx&lt;/code&gt; inside &lt;code&gt;&amp;lt;Head&amp;gt;&lt;/code&gt;. The guide notes that Vercel may cache an old favicon for up to 24 hours; append &lt;code&gt;?v=2&lt;/code&gt; if you need a cache bust.&lt;/p&gt;

&lt;h3&gt;
  
  
  Astro
&lt;/h3&gt;

&lt;p&gt;From the &lt;a href="https://emojifavicons.com/emoji-favicon-for-astro" rel="noopener noreferrer"&gt;Astro guide&lt;/a&gt;, in your shared layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;head&amp;gt;
  &amp;lt;link rel="icon" type="image/svg+xml" href="https://emojifavicons.com/rocket" /&amp;gt;
  &amp;lt;link rel="apple-touch-icon" href="https://emojifavicons.com/apple-touch-icon/rocket" /&amp;gt;
&amp;lt;/head&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Delete &lt;code&gt;public/favicon.svg&lt;/code&gt; if the starter left one. Keep the tags in the persistent layout so View Transitions do not thrash the tab icon.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vue 3 + Vite / Nuxt 3
&lt;/h3&gt;

&lt;p&gt;From the &lt;a href="https://emojifavicons.com/emoji-favicon-for-vue" rel="noopener noreferrer"&gt;Vue guide&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vite:&lt;/strong&gt; edit root &lt;code&gt;index.html&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/svg+xml"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://emojifavicons.com/rocket"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://emojifavicons.com/apple-touch-icon/rocket"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Nuxt 3:&lt;/strong&gt; in &lt;code&gt;nuxt.config.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineNuxtConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;head&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;link&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="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/svg+xml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://emojifavicons.com/rocket&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="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apple-touch-icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://emojifavicons.com/apple-touch-icon/rocket&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="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;h3&gt;
  
  
  WordPress
&lt;/h3&gt;

&lt;p&gt;From the &lt;a href="https://emojifavicons.com/emoji-favicon-for-wordpress" rel="noopener noreferrer"&gt;WordPress guide&lt;/a&gt;, append to the child theme’s &lt;code&gt;functions.php&lt;/code&gt; (or a site-wide snippets plugin):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp_head'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;link rel="icon" href="https://emojifavicons.com/rocket" type="image/svg+xml"&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;link rel="apple-touch-icon" href="https://emojifavicons.com/apple-touch-icon/rocket"&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clear the Customizer Site Icon if WordPress still serves its own. Hard-refresh after saving.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic tab notifications with the JS SDK
&lt;/h2&gt;

&lt;p&gt;Static badges are fine for marketing sites. Apps need the tab to react. Include the SDK once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://emojifavicons.com/sdk.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;https://emojifavicons.com/sdk.js&lt;/code&gt; returns &lt;code&gt;application/javascript&lt;/code&gt;. The homepage and docs show the core surface; the script itself exposes:&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="nx"&gt;EmojiFavicon&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rocket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;EmojiFavicon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fire&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="s1"&gt;pulse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;EmojiFavicon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bell&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// flash, then restore&lt;/span&gt;
&lt;span class="nx"&gt;EmojiFavicon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;EmojiFavicon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBadge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bell&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;EmojiFavicon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearBadge&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A practical pattern for “you have new messages while the tab is in the background”:&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;onUnread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&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="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;EmojiFavicon&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bell&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="na"&gt;badge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;99&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;EmojiFavicon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restore&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;&lt;code&gt;notify(emoji, durationMs)&lt;/code&gt; temporarily swaps the icon, then restores the previous &lt;code&gt;href&lt;/code&gt; — handy for a one-shot alert without managing state yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trade-offs (be honest)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Depends on a third-party URL.&lt;/strong&gt; If emojifavicons.com is unreachable, the tab falls back to whatever the browser does without a favicon. The &lt;a href="https://emojifavicons.com/compare" rel="noopener noreferrer"&gt;compare&lt;/a&gt; page itself lists this as a con of URL-based services, mitigated by CDN caching (docs: 7-day CDN cache, 24-hour browser cache) and by downloading the SVG/PNG/ICO (or the ZIP from &lt;code&gt;/download/:emoji&lt;/code&gt;) and hosting it yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rate limits.&lt;/strong&gt; The FAQ on the homepage says normal use has a &lt;strong&gt;300 requests per minute&lt;/strong&gt; limit, with no signup or credit card required. That is generous for a favicon, but it is still a shared free resource — do not hammer it from a CI loop that rebuilds thousands of pages per minute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not a full custom-image favicon studio.&lt;/strong&gt; If you need a hand-drawn mark or a photo crop, generate and self-host assets instead. For that workflow, &lt;a href="https://findutils.com/design/favicon-generator/" rel="noopener noreferrer"&gt;FindUtils’ favicon generator&lt;/a&gt; is a useful browser-side companion when you already have an image file. EmojiFavicons and the FindUtils generator solve different problems: emoji-as-URL vs. image-to-package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching surprises.&lt;/strong&gt; CDNs and browsers cache favicons aggressively. After a change, hard-refresh or bump a query string.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick health check
&lt;/h2&gt;

&lt;p&gt;Before you ship, run your domain through the &lt;a href="https://emojifavicons.com/check" rel="noopener noreferrer"&gt;Favicon Health Checker&lt;/a&gt;. It scores icon / SVG / apple-touch / manifest coverage and offers copy-paste fixes. Pair that with the interactive &lt;a href="https://emojifavicons.com/generator" rel="noopener noreferrer"&gt;generator&lt;/a&gt; if you want to preview size, shape, animation, and badge before pasting a URL into production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add &lt;code&gt;&amp;lt;link rel="icon" href="https://emojifavicons.com/fire"&amp;gt;&lt;/code&gt; (or your emoji of choice).&lt;/li&gt;
&lt;li&gt;Optionally tune with &lt;code&gt;?size=&lt;/code&gt;, &lt;code&gt;?bg=&lt;/code&gt;, &lt;code&gt;?animate=&lt;/code&gt;, &lt;code&gt;?badge=&lt;/code&gt;, or a combo like &lt;code&gt;/fire+rocket&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Follow the framework guide for &lt;a href="https://emojifavicons.com/emoji-favicon-for-nextjs" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;, &lt;a href="https://emojifavicons.com/emoji-favicon-for-astro" rel="noopener noreferrer"&gt;Astro&lt;/a&gt;, &lt;a href="https://emojifavicons.com/emoji-favicon-for-vue" rel="noopener noreferrer"&gt;Vue&lt;/a&gt;, or &lt;a href="https://emojifavicons.com/emoji-favicon-for-wordpress" rel="noopener noreferrer"&gt;WordPress&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;For runtime swaps, load &lt;code&gt;sdk.js&lt;/code&gt; and call &lt;code&gt;set&lt;/code&gt; / &lt;code&gt;animate&lt;/code&gt; / &lt;code&gt;notify&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you outgrow a third-party URL, download the ZIP and self-host.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Start at &lt;a href="https://emojifavicons.com/" rel="noopener noreferrer"&gt;emojifavicons.com&lt;/a&gt;, browse the &lt;a href="https://emojifavicons.com/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt;, and if you need more free browser-only utilities beyond favicons, &lt;a href="https://findutils.com/" rel="noopener noreferrer"&gt;FindUtils&lt;/a&gt; has a large catalog of client-side tools that never upload your files.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>frontend</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Why Is My Email Going to Spam? A Browser-Only Triage for SPF, DKIM, DMARC, PTR and Blocklists</title>
      <dc:creator>FindUtils</dc:creator>
      <pubDate>Wed, 14 Oct 2026 08:52:54 +0000</pubDate>
      <link>https://dev.to/findutils/why-is-my-email-going-to-spam-a-browser-only-triage-for-spf-dkim-dmarc-ptr-and-blocklists-54b5</link>
      <guid>https://dev.to/findutils/why-is-my-email-going-to-spam-a-browser-only-triage-for-spf-dkim-dmarc-ptr-and-blocklists-54b5</guid>
      <description>&lt;p&gt;Few things are as quietly maddening as a perfectly normal email landing in someone's spam folder. A password reset, an invoice, a reply to a customer: gone, filed next to fake lottery wins. The good news is that most deliverability problems come down to a small set of DNS records and reputation signals, and you can check nearly all of them from a browser in about fifteen minutes.&lt;/p&gt;

&lt;p&gt;This guide walks through the triage in the order I'd actually do it. No terminal, no paid dashboards. Just evidence first, then fixes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 0: Get one real message that went to spam
&lt;/h2&gt;

&lt;p&gt;Don't start by guessing at DNS. Start with a message that actually failed. Ask the recipient to forward it as an attachment, or send a test to a Gmail or Outlook account you control and find it in Junk.&lt;/p&gt;

&lt;p&gt;Then open the full headers ("Show original" in Gmail, "View message source" in Outlook). This raw text is the single most useful thing you'll look at today, because it contains the receiving server's own verdict.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Read the headers before touching anything
&lt;/h2&gt;

&lt;p&gt;Paste the raw headers into the &lt;a href="https://findutils.com/network/email-header-analyzer/" rel="noopener noreferrer"&gt;Email Header Analyzer&lt;/a&gt;. It traces the routing path hop by hop, lists the IP address of each server along the way, and pulls out the SPF, DKIM and DMARC results.&lt;/p&gt;

&lt;p&gt;Look for the &lt;code&gt;Authentication-Results&lt;/code&gt; line. You'll typically see something like &lt;code&gt;spf=pass&lt;/code&gt;, &lt;code&gt;dkim=fail&lt;/code&gt;, &lt;code&gt;dmarc=fail&lt;/code&gt;. That tells you where to dig:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;All three pass, still spam?&lt;/strong&gt; The problem is probably reputation or content, not authentication. Skip ahead to Steps 5 and 6.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SPF fails:&lt;/strong&gt; the sending IP isn't authorized for your domain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DKIM fails or is missing:&lt;/strong&gt; the message wasn't signed, or the signature doesn't match the published key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DMARC fails:&lt;/strong&gt; neither SPF nor DKIM passed &lt;em&gt;in alignment&lt;/em&gt; with the From domain (more on that below).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also write down the IP of the first external hop, the server that actually handed the mail to the internet. You'll need it for the PTR and blocklist checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Check SPF
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; SPF (Sender Policy Framework) is a TXT record on your domain that lists which servers are allowed to send mail for it. It looks like this:&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 -all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run your domain through the &lt;a href="https://findutils.com/security/email-security-checker/" rel="noopener noreferrer"&gt;Email Security Checker&lt;/a&gt;, which inspects SPF, DKIM, DMARC, MTA-STS, TLS-RPT and BIMI in one pass. If you want to see the raw records yourself, the &lt;a href="https://findutils.com/network/dns-lookup/" rel="noopener noreferrer"&gt;DNS Lookup&lt;/a&gt; tool will show every TXT record on the domain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common SPF mistakes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Two SPF records.&lt;/strong&gt; You're allowed exactly one TXT record starting with &lt;code&gt;v=spf1&lt;/code&gt;. Adding a second one when you sign up for a new service is the most common way people break SPF. Merge them into one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Too many lookups.&lt;/strong&gt; SPF has a limit of 10 DNS lookups (each &lt;code&gt;include&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;mx&lt;/code&gt; and &lt;code&gt;redirect&lt;/code&gt; counts, and nested includes count too). Go over and receivers return a &lt;code&gt;permerror&lt;/code&gt;, which often behaves like a fail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forgetting a sender.&lt;/strong&gt; Your newsletter tool, your CRM, your helpdesk, the billing system: every service that sends as your domain needs to be covered, or needs to send from a subdomain with its own record.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using &lt;code&gt;+all&lt;/code&gt;.&lt;/strong&gt; This authorizes the entire internet. Never do it. Use &lt;code&gt;~all&lt;/code&gt; (soft fail) while testing and &lt;code&gt;-all&lt;/code&gt; once you're confident.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 3: Check DKIM
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; DKIM adds a cryptographic signature to each message. The receiving server fetches your public key from DNS at &lt;code&gt;selector._domainkey.yourdomain.com&lt;/code&gt; and verifies the signature.&lt;/p&gt;

&lt;p&gt;The selector is in the &lt;code&gt;DKIM-Signature&lt;/code&gt; header of your message (the &lt;code&gt;s=&lt;/code&gt; value). Look up that exact name as a TXT record with &lt;a href="https://findutils.com/network/dns-lookup/" rel="noopener noreferrer"&gt;DNS Lookup&lt;/a&gt;, or let the Email Security Checker find it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common DKIM mistakes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Signing is never turned on.&lt;/strong&gt; Many providers require you to publish the key &lt;em&gt;and&lt;/em&gt; click an "enable" or "authenticate" button. Publishing the record alone does nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broken key formatting.&lt;/strong&gt; Long keys are sometimes split into multiple strings, and some DNS panels add stray quotes or line breaks. A single missing character invalidates the key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signing with the provider's domain.&lt;/strong&gt; If the signature says &lt;code&gt;d=mailprovider.net&lt;/code&gt; instead of &lt;code&gt;d=yourdomain.com&lt;/code&gt;, DKIM may "pass" but won't align for DMARC.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Check DMARC
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; DMARC is a TXT record at &lt;code&gt;_dmarc.yourdomain.com&lt;/code&gt; that tells receivers what to do when a message fails authentication, and where to send reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight email"&gt;&lt;code&gt;&lt;span class="nt"&gt;v=DMARC1; p=none; rua=mailto&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="na"&gt;dmarc-reports@yourdomain.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key idea is &lt;strong&gt;alignment&lt;/strong&gt;. DMARC passes only if SPF or DKIM passes &lt;em&gt;and&lt;/em&gt; the domain they validated matches the domain in the visible From address. This is why a message can show &lt;code&gt;spf=pass&lt;/code&gt; and still fail DMARC: SPF validated your email provider's bounce domain, not yours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common DMARC mistakes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No record at all.&lt;/strong&gt; Major mailbox providers now expect bulk senders to have DMARC. Even &lt;code&gt;p=none&lt;/code&gt; is better than nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jumping straight to &lt;code&gt;p=reject&lt;/code&gt;.&lt;/strong&gt; If you haven't confirmed every legitimate sender aligns, you'll block your own mail. Start at &lt;code&gt;none&lt;/code&gt;, read the reports, move to &lt;code&gt;quarantine&lt;/code&gt;, then &lt;code&gt;reject&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reports going nowhere.&lt;/strong&gt; A &lt;code&gt;rua&lt;/code&gt; address that no one reads means you're flying blind.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a broader look at your domain's email-related DNS (MX, SPF, DKIM, DMARC together), the &lt;a href="https://findutils.com/network/dns-security-scanner/" rel="noopener noreferrer"&gt;DNS Security Scanner&lt;/a&gt; gives a useful second opinion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Check reverse DNS (PTR)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; A PTR record maps an IP address back to a hostname. Receiving servers check that your sending IP has one, and ideally that the hostname resolves back to the same IP (forward-confirmed reverse DNS).&lt;/p&gt;

&lt;p&gt;Take the sending IP from Step 1 and run it through the &lt;a href="https://findutils.com/network/reverse-dns-lookup/" rel="noopener noreferrer"&gt;Reverse DNS Lookup&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you use Google Workspace, Microsoft 365, or a major sending service, PTR is their job, and it's almost always fine.&lt;/li&gt;
&lt;li&gt;If you run your own mail server or send from a VPS, a missing or generic PTR (something like &lt;code&gt;123-45-67-89.static.isp.net&lt;/code&gt;) is a classic spam trigger. PTR records are set by whoever owns the IP block, so you'll usually configure it in your hosting provider's panel, not your DNS provider's.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 6: Check blocklists
&lt;/h2&gt;

&lt;p&gt;If authentication passes and PTR looks right, reputation is the next suspect. Put the sending IP into the &lt;a href="https://findutils.com/network/ip-blacklist-checker/" rel="noopener noreferrer"&gt;IP Blacklist Checker&lt;/a&gt;, which queries five DNS-based blocklists (Barracuda, SpamCop, UCEPROTECT, JustSpam and PSBL) in one go.&lt;/p&gt;

&lt;p&gt;Be honest with yourself about the results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;On a shared sending service?&lt;/strong&gt; A listing on a shared IP is the provider's problem to solve. Open a ticket with them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On your own server?&lt;/strong&gt; Find out &lt;em&gt;why&lt;/em&gt; before requesting delisting. A compromised account, an open form that sends mail, or a purchased list will get you relisted within days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean everywhere and still in spam?&lt;/strong&gt; Five lists aren't the whole picture. Big providers like Gmail and Microsoft keep their own internal reputation data, and content, engagement and sending volume all matter. Check their postmaster tools for your domain.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 7: Re-check after you change DNS
&lt;/h2&gt;

&lt;p&gt;DNS changes aren't instant. After editing a record, use the &lt;a href="https://findutils.com/network/dns-propagation-checker/" rel="noopener noreferrer"&gt;DNS Propagation Checker&lt;/a&gt; to confirm resolvers around the world see the new value before you conclude a fix "didn't work." Then send a fresh test message and read the headers again. That's the only verdict that really counts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The quick checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Got the raw headers from a real message that went to spam&lt;/li&gt;
&lt;li&gt;[ ] Read &lt;code&gt;Authentication-Results&lt;/code&gt;: which of SPF, DKIM, DMARC failed?&lt;/li&gt;
&lt;li&gt;[ ] Exactly &lt;strong&gt;one&lt;/strong&gt; SPF record, under 10 lookups, covering every sender, ending in &lt;code&gt;~all&lt;/code&gt; or &lt;code&gt;-all&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] DKIM enabled at every sending service, signing with &lt;strong&gt;your&lt;/strong&gt; domain (&lt;code&gt;d=yourdomain.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;[ ] DMARC record exists at &lt;code&gt;_dmarc&lt;/code&gt;, with a &lt;code&gt;rua&lt;/code&gt; address someone actually reads&lt;/li&gt;
&lt;li&gt;[ ] SPF or DKIM &lt;strong&gt;aligns&lt;/strong&gt; with the From domain&lt;/li&gt;
&lt;li&gt;[ ] Sending IP has a PTR that resolves back to the same IP&lt;/li&gt;
&lt;li&gt;[ ] Sending IP isn't on any blocklist (and you know why if it was)&lt;/li&gt;
&lt;li&gt;[ ] DNS changes have propagated before you re-test&lt;/li&gt;
&lt;li&gt;[ ] Sent a fresh test and confirmed the new headers say &lt;code&gt;pass&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A final word
&lt;/h2&gt;

&lt;p&gt;Deliverability problems feel mysterious, but they rarely are. In most cases the headers tell you exactly what failed, and the fix is a single DNS record: a duplicate SPF entry, a DKIM key that never got enabled, a missing DMARC policy. Work through it in order, change one thing at a time, and verify with a real message after each change.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;All the tools linked above are part of &lt;a href="https://findutils.com/" rel="noopener noreferrer"&gt;FindUtils&lt;/a&gt;, a collection of 463 free tools that run in your browser. No sign-up, no installs.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>email</category>
      <category>dns</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>JSON, PDF and Image Chores in the Browser: A No-Upload Workflow Guide</title>
      <dc:creator>FindUtils</dc:creator>
      <pubDate>Tue, 13 Oct 2026 06:40:24 +0000</pubDate>
      <link>https://dev.to/findutils/json-pdf-and-image-chores-in-the-browser-a-no-upload-workflow-guide-4k4i</link>
      <guid>https://dev.to/findutils/json-pdf-and-image-chores-in-the-browser-a-no-upload-workflow-guide-4k4i</guid>
      <description>&lt;p&gt;Most of my "five-minute tasks" are not coding. They are chores: an API response that won't parse, a contract that has to go out as one PDF, a batch of iPhone photos a client can't open. The usual fix is to search for "json formatter online", click the first result, and paste in something you shouldn't.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://findutils.com" rel="noopener noreferrer"&gt;FindUtils&lt;/a&gt; partly because of that habit. It is a free toolbox of single-purpose browser tools: 463 at the time of writing. The core tools need no account and no install. This post is less about the site and more about a workflow: how to get through common JSON, PDF and image chores without handing your files to a stranger's server, and how to check that claim for any tool, including mine.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, how do you know a tool doesn't upload?
&lt;/h2&gt;

&lt;p&gt;"Runs in your browser" is a claim, so test it before you trust it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Open DevTools → Network&lt;/strong&gt; before you drop the file in. Clear the log, run the tool, and look for a request whose size matches your file. A client-side tool may still load scripts, fonts, analytics or ads, but none of those requests should carry your data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the page's own data note.&lt;/strong&gt; A good tool says what leaves the device. On FindUtils, file tools that run locally say so on the page. Lookup tools (DNS, WHOIS, SSL) send the domain you type to the service named on the page, because there's no other way to do a lookup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remember what local processing doesn't cover.&lt;/strong&gt; You still have to trust the page's code, your browser extensions and your device. Client-side is a smaller trust surface, not zero trust.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With that settled, here's the workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  JSON: from "invalid" to typed code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Fix and read it: &lt;a href="https://findutils.com/developers/json-formatter/" rel="noopener noreferrer"&gt;JSON Formatter&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Paste the payload. The error message gives you the line and column. &lt;strong&gt;Auto-Fix&lt;/strong&gt; makes a conservative repair of the three usual suspects: trailing commas, single quotes and unquoted keys. &lt;strong&gt;Format&lt;/strong&gt; pretty-prints with 2, 4 or 8 spaces, &lt;strong&gt;Minify&lt;/strong&gt; compacts it, and &lt;strong&gt;Tree&lt;/strong&gt; gives you a collapsible view for deeply nested responses. On the page, the JSON is processed in your browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Compare two versions: &lt;a href="https://findutils.com/developers/json-diff/" rel="noopener noreferrer"&gt;JSON Diff&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
When staging and production return "the same" response but something breaks, a line diff of pretty-printed JSON is noisy. The default view lists semantic paths (added, removed, modified), so &lt;code&gt;user.address.zip&lt;/code&gt; showing as &lt;em&gt;modified&lt;/em&gt; tells you more than 40 red and green lines. You can switch to a unified, git-style view when you need it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Turn it into types: &lt;a href="https://findutils.com/developers/json-to-typescript/" rel="noopener noreferrer"&gt;JSON to TypeScript&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Paste a real response and get interfaces or type aliases. It's a better starting point than typing the shape by hand, but review optional fields. A sample can't tell you which keys are sometimes missing. There are siblings for &lt;a href="https://findutils.com/developers/json-to-zod-schema/" rel="noopener noreferrer"&gt;Zod&lt;/a&gt;, &lt;a href="https://findutils.com/developers/json-to-go-struct/" rel="noopener noreferrer"&gt;Go structs&lt;/a&gt; and &lt;a href="https://findutils.com/developers/json-to-python-class/" rel="noopener noreferrer"&gt;Python classes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Hand it to a spreadsheet person: &lt;a href="https://findutils.com/convert/json-to-csv/" rel="noopener noreferrer"&gt;JSON ↔ CSV&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Both directions work on one page, with nested objects, quoted commas and custom delimiters handled. One caution: CSV has no types. &lt;code&gt;"00123"&lt;/code&gt; and &lt;code&gt;123&lt;/code&gt; look the same once they're in a cell, so don't round-trip JSON through CSV and expect the original back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bonus: &lt;a href="https://findutils.com/developers/jwt-decoder/" rel="noopener noreferrer"&gt;JWT Decoder&lt;/a&gt;.&lt;/strong&gt; A JWT is often a live credential, so pasting one into a random decoder is a real risk. This one decodes and verifies HS256/384/512 and RS256 (with a PEM key) in the browser, and the page states that secrets stay on the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  PDF: merge, number, clean, in that order
&lt;/h2&gt;

&lt;p&gt;The order matters more than the tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;a href="https://findutils.com/pdf/pdf-merger/" rel="noopener noreferrer"&gt;Merge&lt;/a&gt;&lt;/strong&gt; the files and drag them into sequence. The merge runs in the browser with the open-source pdf-lib library. If you only need part of a document, run &lt;a href="https://findutils.com/pdf/pdf-split/" rel="noopener noreferrer"&gt;Split&lt;/a&gt; first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;a href="https://findutils.com/pdf/pdf-page-numbers/" rel="noopener noreferrer"&gt;Add page numbers&lt;/a&gt;&lt;/strong&gt; &lt;em&gt;after&lt;/em&gt; merging. If you number each file first, you get "1, 2, 3, 1, 2" in the final document. One numbering pass on the merged file gives one sequence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. &lt;a href="https://findutils.com/pdf/pdf-compress/" rel="noopener noreferrer"&gt;Compress&lt;/a&gt;&lt;/strong&gt; if the file is too big for email. &lt;em&gt;Low&lt;/em&gt; rewrites the structure, &lt;em&gt;Medium&lt;/em&gt; re-encodes embedded JPEGs, and &lt;em&gt;High&lt;/em&gt; also scales them down, so photos may look softer. Text stays the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. &lt;a href="https://findutils.com/pdf/pdf-metadata-remover/" rel="noopener noreferrer"&gt;Remove metadata&lt;/a&gt;&lt;/strong&gt; as the very last step. A PDF can carry the author's account name, the program that made it and timestamps. The tool shows the Info fields and XMP data and gives you a copy without the ones you tick. Two honest caveats from the tool's own docs: it is a metadata remover, not a forensic scrub (annotation author names and EXIF inside embedded photos stay), and if you open and re-save the cleaned file in another program, that program writes its own Producer and dates back in. So clean last, then send.&lt;/p&gt;

&lt;p&gt;If the file must be locked, &lt;a href="https://findutils.com/pdf/pdf-protect/" rel="noopener noreferrer"&gt;PDF Protect&lt;/a&gt; adds a password with AES-256 encryption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Images: convert, resize, then compress
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;a href="https://findutils.com/convert/heic-to-jpg/" rel="noopener noreferrer"&gt;HEIC to JPG&lt;/a&gt;&lt;/strong&gt;: iPhone photos often show up as blank thumbnails on Windows or get rejected by upload forms. This tool decodes HEIC with libheif compiled to WebAssembly, right in the tab, and outputs JPG, PNG or WebP. Drop a batch and download a zip.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;a href="https://findutils.com/convert/image-resizer/" rel="noopener noreferrer"&gt;Image Resizer&lt;/a&gt;&lt;/strong&gt;: resize &lt;em&gt;before&lt;/em&gt; you compress. A 4000×3000 photo shown at 800×600 wastes bytes no matter how well you compress it. You can resize by percentage or exact pixels, or use social presets (X, LinkedIn, Instagram, YouTube and more).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. &lt;a href="https://findutils.com/convert/image-compressor/" rel="noopener noreferrer"&gt;Image Compressor&lt;/a&gt;&lt;/strong&gt;: takes JPEG, PNG, WebP and AVIF in, and outputs MozJPEG, WebP, AVIF, or lossless PNG optimized with OxiPNG. One detail people miss: JPEG, WebP and AVIF are lossy at &lt;em&gt;every&lt;/em&gt; quality setting, including 100%. If pixels must not change, choose PNG.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before you publish a photo:&lt;/strong&gt; open it in &lt;a href="https://findutils.com/media/media-info-viewer/" rel="noopener noreferrer"&gt;Media Info Viewer&lt;/a&gt; to see what EXIF data it carries (camera, timestamps, sometimes location).&lt;/p&gt;

&lt;h2&gt;
  
  
  When you need it in a script instead
&lt;/h2&gt;

&lt;p&gt;Browser pages are right for one-off jobs, and anything that needs a local file, the camera or a canvas only works there. For repeatable transforms, FindUtils also has a REST API (387 tools) and an MCP server (388 tools) for AI clients. The catalog is described in an &lt;a href="https://findutils.com/api/openapi.json" rel="noopener noreferrer"&gt;OpenAPI 3.1 spec&lt;/a&gt; and indexed in &lt;a href="https://findutils.com/llms.txt" rel="noopener noreferrer"&gt;llms.txt&lt;/a&gt;. Every page also has a Markdown twin: add &lt;code&gt;markdown.md&lt;/code&gt; to the path.&lt;/p&gt;

&lt;p&gt;To set expectations: running a tool through the API or MCP needs an API key. Keys aren't for sale yet, and early access goes through the contact page. The catalog and schemas are open to read, and the browser tools need no key.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test the "no upload" claim&lt;/strong&gt; with the Network tab instead of trusting a badge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON:&lt;/strong&gt; format → diff → generate types. Don't expect CSV to keep your types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PDF:&lt;/strong&gt; merge → number → compress → strip metadata &lt;em&gt;last&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Images:&lt;/strong&gt; convert → resize → compress, and check EXIF before you publish.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything above is free at &lt;a href="https://findutils.com" rel="noopener noreferrer"&gt;findutils.com&lt;/a&gt;. If a tool is missing from your workflow, there's a &lt;a href="https://findutils.com/request-tool/" rel="noopener noreferrer"&gt;request page&lt;/a&gt;. I read those.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Olgun Özoktaş builds and reviews the tools on FindUtils.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>json</category>
      <category>privacy</category>
    </item>
  </channel>
</rss>
