hosting
How to Host an HTML, CSS & JavaScript Folder
By PreviewSend Team · Jul 17, 2026 · 6 min read

It runs fine when you double-click index.html on your own machine - the styling loads, the images show, the buttons do their thing. Now you want a link someone else can open. That's where most tutorials lose you: they start with git init and a deploy pipeline you never asked for. You don't need either. A folder of plain HTML, CSS, and JavaScript can go online without a build step, as long as it's laid out the way a static host expects and its files use the right paths.
Use a folder layout that works on static hosts
Keep everything the page needs inside one folder, with index.html sitting directly at its top level:
my-site/
├── index.html
├── styles.css
├── script.js
└── images/
└── logo.png
Put index.html at the top level. Static hosts commonly use it as the page for the root URL, so a different filename or an extra wrapper directory can leave the host without an entry page. This is the same conventional site folder MDN describes when it walks through setting up a project: one entry HTML file, with stylesheets, scripts, and assets alongside it.
Subfolders are fine - the images/ directory above is normal. What breaks things is putting index.html inside a subfolder while expecting it to answer at the root.
Link your CSS and JS with relative references
The links between your files are where a working local folder quietly stops working once it's served from a URL. The fix is to make every reference relative to the page, not anchored to the domain root.
Inside index.html, point at the sibling files by name, with no leading slash:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<img src="images/logo.png" alt="Company logo" />
<script src="script.js"></script>
</body>
</html>
href="styles.css" and src="script.js" resolve next to the current page, so they work whether the site lives at the domain root or at example.com/my-project/. The asset in the subfolder follows the same rule: images/logo.png reaches down one level from wherever index.html sits.
Compare that to href="/styles.css". The leading slash tells the browser to start from the domain root. It may work on a local development server at /, then point at the wrong place once the hosted site lives under a subpath. MDN's reference on resolving relative references to a URL shows how the browser resolves a path from the current document, its parent, or the root. If your page loads as unstyled HTML after upload, check for that slash first; the ZIP hosting guide covers the same trap and build-tool base paths in more detail.
Test the folder locally before you upload
You can catch most broken links before the folder ever reaches a host. Open index.html in your browser - double-clicking loads it over file://, which is enough for plain CSS, images, and script tags. Then run these checks:
- Open developer tools and watch the Network tab. Reload and scan for failed requests. A missing local file may show
ERR_FILE_NOT_FOUND; after hosting, the equivalent failure is usually a404. Either way, the requested filename points at the reference that's wrong. - Check the Console tab for errors. A missing script or a JavaScript exception surfaces here rather than in the Network list.
- Confirm filename case matches exactly. Your local machine may treat
Logo.pngandlogo.pngas the same file; most hosts do not. A reference tostyles.csswon't find a file saved asStyles.cssonce it's online. - If the page uses ES modules or
fetch(), test with a local server. Browsers block those overfile://, so double-clicking gives a false failure. Any one-line static server (for examplepython3 -m http.serverrun from inside the folder) serves it overhttp://so the page behaves the way it will when hosted.
A clean Network tab means the folder has cleared the most common local path mistakes. Check the hosted copy once more after uploading, since its URL and filesystem rules can differ from your machine.
Zip the folder so index.html lands at the root
A ZIP turns the folder into one file to upload, and PreviewSend needs one. Don't zip the enclosing my-site directory - that buries index.html a level down in the archive. Enter the folder and compress its contents instead:
cd my-site
zip -r ../site.zip .

The trailing . means "everything in this directory," so index.html sits at the top of site.zip rather than inside a my-site/ wrapper. On macOS or Windows without the command line, open the folder, select all the files inside it, and compress the selection rather than the enclosing folder. That extra wrapper is a common reason hosts reject an upload; if yours already has, fixing an "index.html not found" error covers how to tell which cause you hit.
No-Git upload versus GitHub Pages
Once you have the folder (or its ZIP), two routes get it online without a build pipeline. They differ mostly in whether a Git repository is involved.
A direct upload skips Git entirely. Cloudflare's Pages Direct Upload has a dashboard drop zone that accepts either a folder or a ZIP and publishes to a pages.dev URL. PreviewSend works the same way with a ZIP: upload the archive, get a stable URL back. There's no repository to create and nothing to push - you hand over the files and get a link. To weigh the direct-upload hosts against each other, Netlify Drop alternatives for ZIP website hosting lines them up by what you need the link to do.
GitHub Pages publishes from a repository instead. You create a repo, put the folder's contents in it, and enable Pages on a branch and folder. Per GitHub's own setup docs, the publishing source needs its entry file - index.html, index.md, or README.md - at the top level of the folder you point Pages at, which is the same top-level-entry rule in a different wrapper. It's free and durable, and if you're comfortable with a repo it's a fine home for a public site. But it does assume you're willing to use Git, which is the step this whole approach was meant to avoid.
If your goal is a private or client-facing preview rather than a public site, the direct-upload route has one practical edge: the host can manage search directives and access for you.
On PreviewSend, start a free workspace and upload site.zip (up to 100 MB compressed). Eligible first-time owners get a one-time 14-day Pro trial on their first workspace without entering a card. Afterward, Free limits apply: the most recently updated site stays live and excess sites are archived.
Normal site responses and password challenges include X-Robots-Tag: noindex, nofollow; active projects without a password also serve a robots.txt disallow.
Those directives ask search engines not to index or crawl the page, but they are not access control or an absolute indexing guarantee.
The optional project password is the separate control.
Turn it on and the URL serves a challenge before it serves your files.
Password-protecting a static site explains why a password checked in your own JavaScript cannot do that job.

New projects enable client review by default. Send the stable URL and a client can pin comments or request changes directly on the page without creating an account. Upload the next ZIP to the same project after each revision; the URL stays fixed while the comments remain tied to the version the client reviewed.
FAQ
Does the entry file have to be named index.html?
For broad compatibility, name the entry file index.html and place it at the archive root. PreviewSend can use the shallowest nested index.html, while GitHub Pages also accepts index.md or README.md; other direct-upload hosts may be stricter.
Can I just double-click index.html to test it, or do I need a server?
Double-clicking opens it over file:// and that is enough to check your CSS, images, and plain script tags - they load by relative path the same way they will once hosted. The exception is ES modules (script type="module") and fetch() calls, which browsers block over file://. If your page uses either, run a quick local server to test instead.
The page works on my local server but loses its styling after I upload it. Why?
Check for a leading slash. href="/styles.css" resolves against the domain root, which can work when your local server uses / but fail when the hosted site lives under a subpath. Drop the slash so the reference is relative (href="styles.css"). The ZIP hosting guide covers this failure and base-path fixes in more depth.
Do I need to zip the folder for GitHub Pages too?
No. GitHub Pages publishes files from a repository, so there you push the folder contents into a repo rather than zipping them. The ZIP step is for drag-and-drop and direct-upload hosts that take an archive instead of a Git push.
Have a site to host? Upload it and get a live link in seconds.
Get it free