{"id":7891,"date":"2026-03-09T13:00:33","date_gmt":"2026-03-09T18:00:33","guid":{"rendered":"https:\/\/justinparrtech.com\/JustinParr-Tech\/?p=7891"},"modified":"2026-03-13T17:40:43","modified_gmt":"2026-03-13T22:40:43","slug":"arduino-web-server-add-favicon","status":"publish","type":"post","link":"https:\/\/justinparrtech.com\/JustinParr-Tech\/arduino-web-server-add-favicon\/","title":{"rendered":"UPDATED:  Arduino Web Server &#8211; Add favicon"},"content":{"rendered":"<p>I went down a rabbit hole trying to add a &#8220;favicon&#8221; (Web page icon) to an ESP32 web server project.<\/p>\n<p>No one seems to have a complete answer, so here you go!<\/p>\n<p>UPDATE:\u00a0 After I clicked &#8220;Publish&#8221; on the original version of this article, I started running in to all sorts of problems with my ESP32.<\/p>\n<p>This sent me even deeper in to the rabbit hole, but I ultimately found a good solution.<\/p>\n<p>Here is a step-by-step walk-through (UPDATED).<\/p>\n<p>&nbsp;<\/p>\n<p><!--more--><\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_82_2 counter-hierarchy ez-toc-counter ez-toc-custom ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\"><p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<\/div><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/arduino-web-server-add-favicon\/#summary\" >Summary<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/arduino-web-server-add-favicon\/#step-by-step-solution-svg\" >Step by Step Solution (SVG)<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/arduino-web-server-add-favicon\/#why-inline-png-did-not-pan-out\" >Why Inline PNG Did Not Pan Out<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/arduino-web-server-add-favicon\/#old-step-by-step-solution-uses-base64-encoded-png\" >OLD Step by Step Solution (Uses base64-encoded PNG)<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/arduino-web-server-add-favicon\/#stuff-that-did-not-work\" >Stuff that DID NOT Work<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/arduino-web-server-add-favicon\/#old-detailed-explanation-base64-encoded-png\" >OLD Detailed Explanation (base64-encoded PNG)<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/arduino-web-server-add-favicon\/#conclusion\" >Conclusion<\/a><\/li><\/ul><\/nav><\/div>\n\n<p>&nbsp;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"summary\"><\/span>Summary<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Building a web server on ESP32 is super simple, and is a great way to build a quick and dirty UI for most projects.\u00a0 In fact, I&#8217;m using ESP32 as the basis for v2 of my gate opener web interface.<\/p>\n<p>HOWEVER, when you open the site in a browser, it makes a request for favicon, which is the &#8220;Favorite&#8221; icon &#8211; the website&#8217;s icon that will also be displayed in your bookmarks (used to be called &#8220;favorites&#8221;) list.<\/p>\n<p>Since there are no files, the request to &#8220;\/favicon.ico&#8221; fails with 404.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-7893\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ss-nofavicon-600x342.png\" alt=\"\" width=\"600\" height=\"342\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ss-nofavicon-600x342.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ss-nofavicon-300x171.png 300w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ss-nofavicon-768x438.png 768w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ss-nofavicon.png 905w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>I don&#8217;t know why this bugged me, LOL!\u00a0 But I thought about it a bit and decided I wanted an icon.<\/p>\n<p>&nbsp;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"step-by-step-solution-svg\"><\/span>Step by Step Solution (SVG)<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Overview &#8211; add an icon &lt;LINK&gt; pointing to a SVG file.\u00a0 Then add a webserver handler to serve the SVG file.<\/p>\n<p>SVG is a text-based, vector-based format.<\/p>\n<ol>\n<li>Go design \/ draw \/ photograph your website icon.\u00a0 Save as or convert to SVG format.\u00a0 Save as favicon.svg.h.<\/li>\n<li>Add favicon.svg.h to your project.<\/li>\n<li>Include favicon.svg.1 from your INO file:<\/li>\n<\/ol>\n<pre>#include \"favicon.svg.h\"<\/pre>\n<ol start=\"4\">\n<li>Edit favicon.svg.h to turn it in to a c variable declaration:<\/li>\n<\/ol>\n<pre><span style=\"color: #3366ff;\">const String favicon_svg = F(<\/span><span style=\"color: #ffcc00;\">R\"=====(<\/span><span style=\"color: #00ffff;\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;<\/span>\r\n<span style=\"color: #00ffff;\">&lt;!DOCTYPE svg PUBLIC \"-\/\/W3C\/\/DTD SVG 1.1\/\/EN\" \"http:\/\/www.w3.org\/Graphics\/SVG\/1.1\/DTD\/svg11.dtd\"&gt;<\/span>\r\n<span style=\"color: #00ffff;\">&lt;svg version=\"1.2\" width=\"152.4mm\" height=\"152.4mm\" viewBox=\"0 0 15240 15240\" preserveAspectRatio=\"xMidYMid\" fill-rule=\"evenodd\" stroke-width=\"28.222\" stroke-linejoin=\"round\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" xmlns:ooo=\"http:\/\/xml.openoffice.org\/svg\/export\" xmlns:xlink=\"http:\/\/www.w3.org\/1999\/xlink\" xmlns:presentation=\"http:\/\/sun.com\/xmlns\/staroffice\/presentation\" xmlns:smil=\"http:\/\/www.w3.org\/2001\/SMIL20\/\" xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\" xml:space=\"preserve\"&gt;<\/span>\r\n<span style=\"color: #00ffff;\">&lt;defs class=\"ClipPathGroup\"&gt;<\/span>\r\n<span style=\"color: #00ffff;\">&lt;clipPath id=\"presentation_clip_path\" clipPathUnits=\"userSpaceOnUse\"&gt;<\/span>\r\n<span style=\"color: #00ffff;\">&lt;rect x=\"0\" y=\"0\" width=\"15240\" height=\"15240\"\/&gt;<\/span>\r\n<span style=\"color: #00ffff;\">&lt;\/clipPath&gt; &lt;clipPath id=\"presentation_clip_path_shrink\" clipPathUnits=\"userSpaceOnUse\"&gt;<\/span>\r\n<span style=\"color: #00ffff;\">&lt;rect x=\"15\" y=\"15\" width=\"15210\" height=\"15210\"\/&gt;<\/span>\r\n<span style=\"color: #00ffff;\">&lt;\/clipPath&gt;\r\n&lt;\/defs&gt;<\/span><\/pre>\n<div>\n<pre><span style=\"color: #00ffff;\">...<\/span><\/pre>\n<div>\n<pre><span style=\"color: #00ffff;\">&lt;\/g&gt;<\/span>\r\n<span style=\"color: #00ffff;\">&lt;\/g&gt;<\/span>\r\n<span style=\"color: #00ffff;\">&lt;\/svg&gt;<\/span>\r\n<span style=\"color: #ffcc00;\">)=====\"<\/span><span style=\"color: #3366ff;\">);<\/span><\/pre>\n<\/div>\n<\/div>\n<p>Note:\u00a0 XML must begin ON THE SAME LINE as the variable declaration.\u00a0 If not, the browser complains about XML not starting on the first line.\u00a0 <span style=\"color: #00ffff;\">Cyan text<\/span> above is the original SVG definition.<\/p>\n<ol start=\"5\">\n<li>Modify your HTML header to link to the SVG file:<\/li>\n<\/ol>\n<pre>&lt;!DOCTYPE HTML&gt;\r\n&lt;HTML&gt;&lt;HEAD&gt;\r\n&lt;TITLE&gt;My App v1.0&lt;\/TITLE&gt;\r\n&lt;META http-equiv='refresh' content='$DELAY,.' \/&gt;\r\n<span style=\"color: #00ff00;\">&lt;LINK rel=\"icon\" href=\"\/favicon.svg\" type=\"image\/svg+xcml\"&gt;<\/span>\r\n&lt;\/HEAD&gt;\r\n&lt;BODY&gt;\r\n\r\n\/\/ your HTML here\r\n\r\n&lt;\/BODY&gt;\r\n&lt;\/HTML&gt;<\/pre>\n<ol start=\"6\">\n<li>Create a server handler to serve out the referenced SVG file:<\/li>\n<\/ol>\n<div>\n<pre>void setup(){\r\n\r\n  \/\/*** Other setup code ***\r\n\r\n  \/\/HTTP Server\r\n  server.on(\"\/\" , normal_handler);\u00a0 \u00a0 \/\/this is your \r\n  server.on(\"\/favicon.svg\" , handleFavicon);\u00a0 \/\/favicon handler\r\n  server.begin();\r\n\r\n  \/\/*** Other setup code \r\n}\r\n\r\n...\r\n\r\nvoid handleFavicon(void){\r\n  server.send(200,\"image\/svg+xml\",<span style=\"color: #3366ff;\">favicon_svg<\/span>);\r\n}<\/pre>\n<\/div>\n<p>The pay careful attention to the content types &#8211; unless everything is configured properly, the browser will refuse it.\u00a0 &#8220;favicon_svg&#8221; is the variable we are declaring in &#8220;favicon.svg.h&#8221;.<\/p>\n<p>&nbsp;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"why-inline-png-did-not-pan-out\"><\/span>Why Inline PNG Did Not Pan Out<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>At first, things worked great.\u00a0 As I started adding more content, I realized that the ESP became increasingly unstable.<\/p>\n<p>After MUCH troubleshooting, here is the problem:<\/p>\n<ul>\n<li>server.send() writes the ENTIRE document as a single string and sends it back to the client.\u00a0 There is probably a way to break this up in to two or more strings, and then store each in its own variable, but this is way more complicated than using server.send()<\/li>\n<li>As you build the HTML, every time you use a String function to concatenate or replace, behind the scenes it&#8217;s creating a NEW string and then copying all of the data from the other strings.<\/li>\n<li>With 3000 extra bytes of base64-encoded PNG in there, it was having to copy the icon over and over and over in memory just to serve up a simple application.<\/li>\n<\/ul>\n<p>Using the code above and SVG format, the response to the client can be much more easily stored in a single variable without thousands of bytes of overhead.\u00a0 When the browser asks for favicon.svg, the extra handler we set up serves it directly from PROGMEM without even having to copy it.\u00a0 Well&#8230;maybe the server object copies it in to a buffer, not sure.\u00a0 But WE don&#8217;t have to copy it.\u00a0 So even though the client may request it repeatedly, the ESP just keeps serving the same static content, with no string manipulation required.<\/p>\n<p>The other problem I think I was having with sending raw PNG is that I suspect server.send(), expecting a string, can&#8217;t handle binary data.\u00a0 Since SVG is explicitly text, it doesn&#8217;t have that problem with SVG.<\/p>\n<p>Another nice thing about linking the SVG is that you can test it by going to http:\/\/[whateverYourESPipAddressIs]\/favicon.svg &#8211; this should display the favicon in your browser.<\/p>\n<p>&nbsp;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"old-step-by-step-solution-uses-base64-encoded-png\"><\/span>OLD Step by Step Solution (Uses base64-encoded PNG)<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Overview &#8211; add an icon &lt;LINK&gt; reference containing an inline, base64-encoded PNG to the document header.<\/p>\n<ol>\n<li>Go design \/ borrow \/ draw \/ photograph your website icon.\u00a0 48&#215;48 pixels is an appropriate resolution.\u00a0 Save as PNG format or convert to PNG.\u00a0 The rest of the steps assume the icon file is saved as <strong>favicon.png<\/strong>.<\/li>\n<li>Convert to base64.<\/li>\n<\/ol>\n<pre>base64 favicon.png &gt; favicon.h<\/pre>\n<ol start=\"3\">\n<li>Add favicon.h to your project.\u00a0 Be sure to add &#8220;include&#8221; to your main INO file:<\/li>\n<\/ol>\n<pre>#include \"favicon.h\"<\/pre>\n<ol start=\"4\">\n<li>Edit the base64 file (favicon.h) to add a variable declaration and HTML syntax:<\/li>\n<\/ol>\n<div>\n<pre><span style=\"color: #3366ff;\">const String favicon = F(<\/span><span style=\"color: #ffcc00;\">R\"=====(<\/span><span style=\"color: #00ff00;\">&lt;link href=\"data:image\/x-icon;base64,<\/span>\r\n<span style=\"color: #00ffff;\">iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAAwnpUWHRSYXcgcHJvZmlsZSB0eXBl<\/span>\r\n<span style=\"color: #00ffff;\">IGV4aWYAAHjabVDBEcMgDPt7io4AyBB7HNKkd92g49eAk4a2ukMRlk8xpv31fNCtIUUmzosULSUY<\/span>\r\n<span style=\"color: #00ffff;\">WFlTNSFhoHaOgTt3wC27T3U6jWQlfDqleP9Rj2fA+FRT+RIkdzfW2VD2fPkKSj5Zm6jpzYPUg5CG<\/span>\r\n<span style=\"color: #00ffff;\">ET2gjmeForJcn7DuYYaMQ41Y5rF\/7ottb8v2H6S0IyIYAzwGQDuFUE2gs1qjNZlmqDGgHmYL+ben<\/span>\r\n<span style=\"color: #00ffff;\">A\/QG3cVZGvmEWAMAAAGFaUNDUElDQyBwcm9maWxlAAB4nH2Rv0vDQBzFX1ulRSoOVhB1yFB1sYuK<\/span>\r\n<span style=\"color: #00ffff;\">ONYqFKFCqBVadTC59Bc0aUhSXBwF14KDPxarDi7Oujq4CoLgDxD\/AHFSdJESv5cUWsR4cNyHd\/ce<\/span>\r\n<span style=\"color: #00ffff;\">d+8Af6PCVLMrDqiaZaSTCSGbWxWCrwgghAGMY1hipj4niil4jq97+Ph6F+NZ3uf+HL1K3mSATyCO<\/span>\r\n<span style=\"color: #00ffff;\">M92wiDeIZzYtnfM+cYSVJIX4nHjCoAsSP3JddvmNc9FhP8+MGJn0PHGEWCh2sNzBrGSoxNPEUUXV<\/span>\r\n<span style=\"color: #00ffff;\">KN+fdVnhvMVZrdRY6578heG8trLMdZojSGIRSxAhQEYNZVRgIUarRoqJNO0nPPxDjl8kl0yuMhg5<\/span>\r\n<span style=\"color: #00ffff;\">FlCFCsnxg\/\/B727NwtSkmxROAN0vtv0xCgR3gWbdtr+Pbbt5AgSegSut7a82gNlP0uttLXoE9G0D<\/span>\r\n<span style=\"color: #00ffff;\">F9dtTd4DLneAwSddMiRHCtD0FwrA+xl9Uw7ovwV61tzeWvs4fQAy1FXqBjg4BMaKlL3u8e5QZ2\/\/<\/span>\r\n<span style=\"color: #00ffff;\">nmn19wOXfXK13okkWAAADXhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw\/eHBhY2tldCBiZWdp<\/span>\r\n<span style=\"color: #00ffff;\">bj0i77u\/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6<\/span>\r\n<span style=\"color: #00ffff;\">eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDQuNC4wLUV4aXYyIj4KIDxyZGY6<\/span>\r\n<span style=\"color: #00ffff;\">UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5z<\/span>\r\n<span style=\"color: #00ffff;\">IyI+CiAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgIHhtbG5zOnhtcE1NPSJodHRw<\/span>\r\n<span style=\"color: #00ffff;\">Oi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5h<\/span>\r\n<span style=\"color: #00ffff;\">ZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgIHhtbG5zOmRjPSJodHRw<\/span>\r\n<span style=\"color: #00ffff;\">Oi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgIHhtbG5zOkdJTVA9Imh0dHA6Ly93d3cu<\/span>\r\n<span style=\"color: #00ffff;\">Z2ltcC5vcmcveG1wLyIKICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8x<\/span>\r\n<span style=\"color: #00ffff;\">LjAvIgogICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICB4bXBN<\/span>\r\n<span style=\"color: #00ffff;\">TTpEb2N1bWVudElEPSJnaW1wOmRvY2lkOmdpbXA6Mzk0OTdlOTgtMDNlNy00YzhjLWE3OTUtYzA3<\/span>\r\n<span style=\"color: #00ffff;\">MTU0YmMzMjNhIgogICB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjBkOGIwMDU1LWY1OGQtNDMx<\/span>\r\n<span style=\"color: #00ffff;\">Mi1iZjQyLTJiMzEwODc1NjJmOCIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlk<\/span>\r\n<span style=\"color: #00ffff;\">Ojk1OGM2YjAzLTc5NmUtNDlkOC04NTE0LTczMzdmNDBiNWMzMiIKICAgZGM6Rm9ybWF0PSJpbWFn<\/span>\r\n<span style=\"color: #00ffff;\">ZS9wbmciCiAgIEdJTVA6QVBJPSIyLjAiCiAgIEdJTVA6UGxhdGZvcm09IkxpbnV4IgogICBHSU1Q<\/span>\r\n<span style=\"color: #00ffff;\">OlRpbWVTdGFtcD0iMTc3MzA3MTM3OTY2MDIwOCIKICAgR0lNUDpWZXJzaW9uPSIyLjEwLjM0Igog<\/span>\r\n<span style=\"color: #00ffff;\">ICB0aWZmOk9yaWVudGF0aW9uPSIxIgogICB4bXA6Q3JlYXRvclRvb2w9IkdJTVAgMi4xMCIKICAg<\/span>\r\n<span style=\"color: #00ffff;\">eG1wOk1ldGFkYXRhRGF0ZT0iMjAyNjowMzowOVQxMDo0OTozOS0wNTowMCIKICAgeG1wOk1vZGlm<\/span>\r\n<span style=\"color: #00ffff;\">eURhdGU9IjIwMjY6MDM6MDlUMTA6NDk6MzktMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAg<\/span>\r\n<span style=\"color: #00ffff;\">IDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">c3RFdnQ6Y2hhbmdlZD0iLyIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpkMjlmYzAw<\/span>\r\n<span style=\"color: #00ffff;\">Ni03Mjk1LTQ3OTYtYmU3YS1kNTM3MTFkODAxYzIiCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9<\/span>\r\n<span style=\"color: #00ffff;\">IkdpbXAgMi4xMCAoTGludXgpIgogICAgICBzdEV2dDp3aGVuPSIyMDI2LTAzLTA5VDEwOjQ5OjM5<\/span>\r\n<span style=\"color: #00ffff;\">LTA1OjAwIi8+CiAgICA8L3JkZjpTZXE+CiAgIDwveG1wTU06SGlzdG9yeT4KICA8L3JkZjpEZXNj<\/span>\r\n<span style=\"color: #00ffff;\">cmlwdGlvbj4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAK<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg<\/span>\r\n<span style=\"color: #00ffff;\">IAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI\/PmARca0AAADq<\/span>\r\n<span style=\"color: #00ffff;\">UExURfvUM\/\/\/\/wAAABQVGAAAFv\/YNP\/aNP\/cNfvTKfvSHf\/bNPvRFfvSJPvTLQAAFRETGPvXRP3o<\/span>\r\n<span style=\"color: #00ffff;\">n\/7xxvzihP\/99\/zjif\/66v700v744\/bQMvzkjvzgePvVOPvaWv733v\/++v3rrQsPGPzebP3po9e2<\/span>\r\n<span style=\"color: #00ffff;\">LpmCJv3uu7qeKgAJF8OlKP7yyf701PvZUOrGMSwoGk1DHeXBLjsyDF9RE3NhFx0dGUQ7HIx3JKmP<\/span>\r\n<span style=\"color: #00ffff;\">KGlaIFlMHn1rIvzecSchCIFtGhEOA413HC0mCc+vKjozG1NIHbCWKTArGiUjGXNiIUc8Dh0YBr6g<\/span>\r\n<span style=\"color: #00ffff;\">JlJGEDQsC0I4DYFzfZMAAAABYktHRACIBR1IAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH<\/span>\r\n<span style=\"color: #00ffff;\">6gMJDzEnKahS+gAAAcZJREFUSMetlm1vwiAQx0\/4AJqW1KAkrYsP2YTE2fiQNLUh+kLnvv\/nWWun<\/span>\r\n<span style=\"color: #00ffff;\">BUprcfu\/Anq\/chzHAfRMkWE0Sjhjg7dRNCS1z2BYez4DRcz3SAtAIg418Yg0AOHcYn5D5qENCHxo<\/span>\r\n<span style=\"color: #00ffff;\">lB\/UgSWHFvGlCXgMWsU8HXhmXxElsHxqnxPLCgg4dBAP7kA4hk7yw19gDh01LwGiOUTltOpMJdWc<\/span>\r\n<span style=\"color: #00ffff;\">Ijcg0uz3GM\/unS3Ge42ICkCfYIcx7suyLfM23hlTQM\/TJtgURtvHBBhvtCm8HNBSSEwKo2PZORbt<\/span>\r\n<span style=\"color: #00ffff;\">idAC1QPCXABGYAh1l9Kyk9Zdys0jPdTFonHjovM4wUgfEHvc3z7C2sd7oX8fQWLsZtvG5UqAg5M4<\/span>\r\n<span style=\"color: #00ffff;\">MDeAuQPOLiVuQKKElYomK0GVsFYbN1s3EDTOlI2rUkOi2ErQDEklNZTk26HryuJPijI1+ZT0zn91<\/span>\r\n<span style=\"color: #00ffff;\">OZlbK88opmp6qwcoJ9B5IdQ8iRFSl+YZR5TuDghd4pNcCbGavmefCKGUGkdULwLyijTpPka2MrM4<\/span>\r\n<span style=\"color: #00ffff;\">V+aHbGUpM2Yho3K2\/j6gyzn9EtZCZiuVVAhaGxyHrxZj93Lf6UL5+NuV5X4ptl+74+A\/Lnb3p8ML<\/span>\r\n<span style=\"color: #00ffff;\">j5Pq+TNgjCfW588PLVM0F+a3EicAAAAASUVORK5CYII=<\/span><span style=\"color: #00ff00;\">\"\r\nrel=\"icon\" type=\"image\/x-icon\" \/&gt;<\/span>\r\n<span style=\"color: #ffcc00;\">)=====\"<\/span><span style=\"color: #3366ff;\">);<\/span><\/pre>\n<\/div>\n<ol start=\"5\">\n<li>Add favicon to the HTML header:<\/li>\n<\/ol>\n<pre>String html=\"\";\r\nhtml+=\"&lt;!DOCTYPE HTML&gt;&lt;HEAD&gt;\";\r\nhtml+=\"&lt;TITLE&gt;My App!&lt;\/TITLE&gt;\";\r\n<span style=\"color: #3366ff;\">html+=favicon;<\/span>\r\nhtml+=\"&lt;\/HEAD&gt;\";\r\nhtml+=\"&lt;BODY&gt;\";\r\n\r\n...app code...\r\n\r\nhtml+=\"&lt;\/BODY&gt;&lt;\/HTML&gt;\";\r\n\r\n<span style=\"color: #999999;\">\/\/send html to client<\/span>\r\nserver.send(200,text\/html,html);<\/pre>\n<p>Since the header of EVERY page must include the favicon link, I have my header declared statically, and do a string replace.<\/p>\n<p>Here is the result:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-7892\" src=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ss-favicon-600x331.png\" alt=\"\" width=\"600\" height=\"331\" srcset=\"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ss-favicon-600x331.png 600w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ss-favicon-300x166.png 300w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ss-favicon-768x424.png 768w, https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-content\/uploads\/ss-favicon.png 904w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>Note:<\/p>\n<ul>\n<li>Correct icon in upper-left<\/li>\n<li>No failed call to favicon.ico<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"stuff-that-did-not-work\"><\/span>Stuff that DID NOT Work<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>I tried adding a server handler for \/favicon.ico and then passing binary file data back to the client.\u00a0 This fails for some uknown reason.\u00a0 I tried multiple file formats and variable declaration formats, and honestly couldn&#8217;t get it to work.<\/p>\n<p>The method above is simple and reliable.\u00a0 Unfortunately, it adds 4k to the payload on every page refresh, but that&#8217;s not a huge amount of overhead.<\/p>\n<p>UPDATE:\u00a0 Add this to the list of stuff that did not work.<\/p>\n<p>&nbsp;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"old-detailed-explanation-base64-encoded-png\"><\/span>OLD Detailed Explanation (base64-encoded PNG)<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Looking at the color-coded code sample above:<\/p>\n<table>\n<tbody>\n<tr>\n<th>Color<\/th>\n<th>Meaning<\/th>\n<td><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\"><span style=\"color: #3366ff;\">\u2588<\/span><\/td>\n<td>c++ variable declaration.<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\"><span style=\"color: #ffcc00;\">\u2588<\/span><\/td>\n<td>c++ syntax for preformatted, multiliine string<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\"><span style=\"color: #00ff00;\">\u2588<\/span><\/td>\n<td>HTML syntax<\/td>\n<td><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\"><span style=\"color: #00ffff;\">\u2588<\/span><\/td>\n<td>base64-encoded favicon.png<\/td>\n<td><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>C declaration:<\/p>\n<ul>\n<li>&#8220;favicon&#8221; is declared as a String type.<\/li>\n<li>&#8220;const&#8221; is used because the string is immutable (non-variable variable)<\/li>\n<li>&#8220;F( )&#8221; function is specific to Arduino.\u00a0 This keeps the string value in non-volatile (PROGMEM) memory rather than copying to RAM.<\/li>\n<\/ul>\n<p>Preformatted String:<\/p>\n<ul>\n<li>&#8220;R&#8221; combined with &#8220;=====(&#8221; tells C that this is a multiline (pre-formatted) string constant.<\/li>\n<li>The string constant ends with )=====&#8221;<\/li>\n<li>&#8220;=====&#8221; can be any 5 character string literal which book-ends the string constant.<\/li>\n<li>For example, &#8220;RABCDEF(String Constant)ABCDEF&#8221; works the same as &#8220;R=====(String Constant)=====&#8221;<\/li>\n<li>PHP syntax is similar.<\/li>\n<\/ul>\n<p>HTML syntax:<\/p>\n<ul>\n<li>&lt;LINK&gt; tag is a general-purpose mechanism for including content from another file.<\/li>\n<li>&#8220;rel&#8221; tells the browser what KIND of link, in this case &#8220;icon&#8221;.<\/li>\n<li>&#8220;type&#8221; reiterates the content-type of the linked file.<\/li>\n<li>in [href=&#8221;data:image\/x-icon;base64,]\n<ul>\n<li>&#8220;href&#8221; points to the location of the linked file.\u00a0 Normally, this would be a URL.<\/li>\n<li>However, &#8220;data:&#8221; tells the browser that the file&#8217;s data is inline.<\/li>\n<li>&#8220;image\/x-icon&#8221; is the content type.<\/li>\n<li>&#8220;base64&#8221; specifies that the file&#8217;s data is encoded as base64.<\/li>\n<li>Everything after the comma &#8220;,&#8221; is the base64 data<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Base64 encoding:<\/p>\n<ul>\n<li>Three 8-bit bytes of data are encoded as four 6-bit groupings<\/li>\n<li>Base64 uses ONLY printable characters and is meant to be completely portable across machine types and implementations.<\/li>\n<li>Uses characters &#8220;0-9&#8221;, &#8220;A-Z&#8221;, &#8220;a-z&#8221;, &#8220;\/&#8221;, &#8220;+&#8221;, and &#8220;=&#8221;<\/li>\n<li>Used for any application where a binary file needs to be embedded in to a readable or printable file, including e-mail, HTML, PKI, and LDAP.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"conclusion\"><\/span>Conclusion<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Having gone through this entire process TWICE now, once with PNG and once with SVG, it just reminds me that arbitrary standards sometimes make things WAY more difficult than they should be.\u00a0 For example, Chrome WILL ALWAYS cache a page, even if it&#8217;s supposed to be dynamic, even if you put cache-control headers in place.\u00a0 But it REFUSES to cache favicon.ico, which should be completely static.\u00a0 If this was not the case, I could have served up a static header that includes the favicon, then redirect to a dynamic page.\u00a0 But because of this flaw, the dynamic page will re-request the favicon, even though Chrome already has it in cache.<\/p>\n<p>Thank you for reading, and I hope you find this helpful!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I went down a rabbit hole trying to add a &#8220;favicon&#8221; (Web page icon) to an ESP32 web server project. No one seems to have a complete answer, so here you go! UPDATE:\u00a0 After I clicked &#8220;Publish&#8221; on the original version of this article, I started running in to all sorts of problems with my [&hellip;]<\/p>\n","protected":false},"author":16,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,22],"tags":[32,31,35,36,33,34],"class_list":["post-7891","post","type-post","status-publish","format-standard","hentry","category-tech-support","category-tech-tip","tag-arduino","tag-esp32","tag-favicon","tag-favicon-ico","tag-webserver","tag-wifi-webserver"],"_links":{"self":[{"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/posts\/7891","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/users\/16"}],"replies":[{"embeddable":true,"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/comments?post=7891"}],"version-history":[{"count":10,"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/posts\/7891\/revisions"}],"predecessor-version":[{"id":7906,"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/posts\/7891\/revisions\/7906"}],"wp:attachment":[{"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/media?parent=7891"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/categories?post=7891"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/justinparrtech.com\/JustinParr-Tech\/wp-json\/wp\/v2\/tags?post=7891"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}