<?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: veida</title>
    <description>The latest articles on DEV Community by veida (@veida).</description>
    <link>https://dev.to/veida</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%2F4113268%2Fb3c3e3ae-a9a3-44bc-9e6a-1c8d33e275bc.png</url>
      <title>DEV Community: veida</title>
      <link>https://dev.to/veida</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/veida"/>
    <language>en</language>
    <item>
      <title>I shipped the same static page to 8 free hosts - here is what each one does to your HTML</title>
      <dc:creator>veida</dc:creator>
      <pubDate>Mon, 07 Sep 2026 12:18:07 +0000</pubDate>
      <link>https://dev.to/veida/i-shipped-the-same-static-page-to-8-free-hosts-here-is-what-each-one-does-to-your-html-4b3g</link>
      <guid>https://dev.to/veida/i-shipped-the-same-static-page-to-8-free-hosts-here-is-what-each-one-does-to-your-html-4b3g</guid>
      <description>&lt;p&gt;I needed one small static page live on as many independent free hosts as I could get, and I wanted to know what each host actually does to the HTML you hand it. So I shipped the same 4.6 KB page to eight of them in a day and measured the result on every one.&lt;/p&gt;

&lt;p&gt;The page itself is real: a single-purpose image generator with a working embed on it, backed by the free anonymous tier of &lt;a href="https://veida.ai/?utm_source=devto&amp;amp;utm_medium=article" rel="noopener noreferrer"&gt;Veida&lt;/a&gt;. That mattered for the test, because a few of these hosts rewrite what you upload, and an iframe is the first thing to break.&lt;/p&gt;

&lt;h2&gt;
  
  
  The eight, and what survived
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Host&lt;/th&gt;
&lt;th&gt;HTML preserved&lt;/th&gt;
&lt;th&gt;Note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*.static.hf.space&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;verbatim&lt;/td&gt;
&lt;td&gt;Hugging Face Space, &lt;code&gt;sdk: static&lt;/code&gt;. Push with git.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*.pages.dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;verbatim&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;wrangler pages deploy&lt;/code&gt;. Unknown paths fall back to &lt;code&gt;index.html&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*.workers.dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;verbatim&lt;/td&gt;
&lt;td&gt;A Worker that returns the HTML string. Separate suffix from Pages.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;user&amp;gt;.github.io&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;verbatim&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;.nojekyll&lt;/code&gt;, or anything starting with &lt;code&gt;_&lt;/code&gt; disappears.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*.surge.sh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;verbatim&lt;/td&gt;
&lt;td&gt;One command. Writes a &lt;code&gt;CNAME&lt;/code&gt; into your build dir - delete it before the next deploy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*.netlify.app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;verbatim&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Defaults to private.&lt;/strong&gt; See below.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*.lovable.app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;verbatim&lt;/td&gt;
&lt;td&gt;It is an AI app builder, so this one surprised me.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The five things that actually cost me time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Netlify projects are private by default now.&lt;/strong&gt; The deploy reports &lt;code&gt;state: ready&lt;/code&gt;, the API says the site is fine, and the public URL returns &lt;strong&gt;401&lt;/strong&gt; with no &lt;code&gt;WWW-Authenticate&lt;/code&gt; header. Nothing in the deploy output hints at it. It is a per-project setting in the UI: Project -&amp;gt; Configuration -&amp;gt; Visitor access -&amp;gt; Public. I spent twenty minutes checking my zip before I checked the setting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Public Suffix List membership decides whether these are separate sites at all.&lt;/strong&gt; &lt;code&gt;pages.dev&lt;/code&gt;, &lt;code&gt;workers.dev&lt;/code&gt;, &lt;code&gt;netlify.app&lt;/code&gt;, &lt;code&gt;github.io&lt;/code&gt;, &lt;code&gt;lovable.app&lt;/code&gt;, &lt;code&gt;hf.space&lt;/code&gt; and &lt;code&gt;static.hf.space&lt;/code&gt; are all on the &lt;a href="https://publicsuffix.org/list/" rel="noopener noreferrer"&gt;PSL&lt;/a&gt;. &lt;code&gt;surge.sh&lt;/code&gt; is not - every &lt;code&gt;*.surge.sh&lt;/code&gt; is treated as one site. If anything you are doing depends on those subdomains being independent, check the list first; it is one &lt;code&gt;grep&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. &lt;code&gt;github.io&lt;/code&gt; is one site per username, not per repo.&lt;/strong&gt; Adding a second repo with Pages enabled does not give you a second independent site, so this host is a one-shot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Cloudflare Pages soft-404s.&lt;/strong&gt; A path that does not exist returns &lt;strong&gt;200&lt;/strong&gt; with your &lt;code&gt;index.html&lt;/code&gt; rather than a 404. Fine for a one-page site, worth knowing before you rely on 404s for anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Lovable will hand back your HTML byte for byte - if you ask precisely.&lt;/strong&gt; I expected an AI builder to rewrite everything into React with its own design system. It did not: the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block, the &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; and every &lt;code&gt;&amp;lt;a href&amp;gt;&lt;/code&gt; came through unchanged, and it added no canonical tag. Two things about driving it, though:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The composer is a &lt;code&gt;contenteditable&lt;/code&gt;, and it ignores &lt;code&gt;beforeinput&lt;/code&gt; with &lt;code&gt;inputType: insertText&lt;/code&gt; - my 5 KB prompt went in as &lt;strong&gt;one character&lt;/strong&gt;, silently. Dispatching a real &lt;code&gt;ClipboardEvent('paste')&lt;/code&gt; with a populated &lt;code&gt;DataTransfer&lt;/code&gt; worked first try.&lt;/li&gt;
&lt;li&gt;The default mode is chat, not build. My first prompt produced a conversation with suggestion chips and no project at all. Saying "create a new project for this ... then publish it" in the message is what actually spawned one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Verification is where the real trap is
&lt;/h2&gt;

&lt;p&gt;Three pages I checked this week returned a normal-looking 200 to plain &lt;code&gt;curl&lt;/code&gt; and &lt;strong&gt;zero&lt;/strong&gt; of my links, for three different reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npmjs.com&lt;/code&gt; returns &lt;strong&gt;403&lt;/strong&gt; to a non-browser user agent.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pypi.org&lt;/code&gt; returns a ~3 KB "Client Challenge" page.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lobehub.com&lt;/code&gt; serves &lt;strong&gt;markdown instead of HTML&lt;/strong&gt; when the user agent is not a browser - status 200, real content, &lt;code&gt;&amp;lt;a href&amp;gt;&lt;/code&gt; count zero.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All three look identical to "the page is live but nothing linked". If you check deployed pages programmatically, send a browser user agent and count anchors, and be suspicious of a clean 200 with an empty result.&lt;/p&gt;

&lt;h2&gt;
  
  
  The page I was deploying
&lt;/h2&gt;

&lt;p&gt;Each host got a different single-purpose page rather than a copy of the same one - eight identical sites would just be eight near-duplicates. Each embeds the generator from the &lt;a href="https://veida.ai/image?utm_source=devto&amp;amp;utm_medium=article" rel="noopener noreferrer"&gt;free AI image generator&lt;/a&gt; so the page does something rather than describing something, and its free tier runs without an account, which is what made an embed viable at all. The prompts I seeded them with came from the &lt;a href="https://veida.ai/image/prompts?utm_source=devto&amp;amp;utm_medium=article" rel="noopener noreferrer"&gt;prompt library&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Check the Public Suffix List before you assume two subdomains are two sites.&lt;/li&gt;
&lt;li&gt;After every deploy, fetch the public URL with a browser UA and count what you expect to be there. Every failure in this batch was silent.&lt;/li&gt;
&lt;li&gt;An AI app builder is a viable static host if you tell it not to build an app.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>devops</category>
      <category>showdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
