Publishing a static site
Documentation, a landing page or a business card site on your own domain — from connecting the domain to automatic publishing from GitHub Actions.
Any site made of ready-to-serve files can be hosted: static site generator output, landing pages and business card sites written by hand in HTML/CSS/JS.
You’ll need:
- a domain and access to your registrar or DNS provider;
- a ready site — a folder with
index.htmlat its root.
How to publish
Section titled “How to publish”| Site | Build | How to upload | Auto-publishing |
|---|---|---|---|
| Business card, a few HTML files | not needed | drag files into the panel | not needed |
| Landing page | depends | ZIP archive or CLI | optional |
| Generators, documentation | required | CLI | yes, step 7 |
1. Prepare the files
Section titled “1. Prepare the files”Upload the build output, not the sources.
| Generator | Command | Folder | Preset | Clean URLs |
|---|---|---|---|---|
| No generator | — | site folder | Plain HTML | Directory index |
| Hugo | hugo --minify |
public/ |
Hugo | Directory index |
| Astro, Starlight | npm run build |
dist/ |
Astro | Directory index |
| Docusaurus | npm run build |
build/ |
Plain HTML | Directory index |
| MkDocs | mkdocs build |
site/ |
Plain HTML | Directory index |
| VitePress | npm run docs:build |
docs/.vitepress/dist/ |
Plain HTML | HTML extension |
| Eleventy | npx @11ty/eleventy |
_site/ |
Plain HTML | Directory index |
| Jekyll | bundle exec jekyll build |
_site/ |
Jekyll | Directory index |
| Gatsby | npm run build |
public/ |
Gatsby | Directory index |
Next.js with output: 'export' |
npm run build |
out/ |
Next.js (export) | HTML extension |
| Vite (React, Vue, Svelte) | npm run build |
dist/ |
SPA | Exact path only |
Check the build folder:
index.htmlis at the root, not in a subfolder;- there is a
404.html— it’s shown for addresses that don’t exist.
2. Connect the domain
Section titled “2. Connect the domain”Pick one option:
- A. Nameservers at WebShield. The domain’s DNS moves to us. The site works on both
wwwandexample.comwithout manual records. Suits zones not tied to another provider. - B. DNS stays with you. Nameservers don’t change; the names you need are connected with records at your current provider. Suits zones hosted in Cloudflare, Route 53 and the like.
Option A: nameservers at WebShield
Section titled “Option A: nameservers at WebShield”-
Control panel → Domains → Add domain, enter
example.com. -
Keep record import on and review the list it finds: mail and other service records must be there.
-
At your registrar, replace the nameservers with these two — no others:
nsbox.webshield.pronshub.webshield.pro -
In Domains, click Check. The status you need is Delegated. It usually takes a couple of hours, but can take up to two days depending on your registrar.
For a site on www, the DNS section must have a www record of type A or CNAME with any value, for example CNAME example.com. Publishing switches the record to WebShield; taking the site down restores the previous value. example.com needs no record.
The name must not have Proxy enabled: a name works either as a protected server or as a static site.
More in Transfer domain control.
Option B: DNS stays with you
Section titled “Option B: DNS stays with you”-
Control panel → Domains → Add domain, enter
example.com— the domain itself, withoutwww. -
Choose Keep your DNS and add records. The Hosts section opens.
-
Add a name →
www→ Host a static site. -
At your DNS provider, add the record from the name’s card:
_acme-challenge.www.example.com. CNAME <address from the card> -
Click Check and wait for the certificate — a few minutes.
Apex domains and ALIAS records are covered in Connect without changing nameservers.
3. Create the site
Section titled “3. Create the site”- Option A: Sites → New site → pick the DNS name. Names with proxying or an existing site can’t be picked.
- Option B: the site is created automatically once the name is verified and is already listed under Sites.
A new site has the Draft status and is not visible to visitors.
4. Configure serving
Section titled “4. Configure serving”Open the site with Manage.
- Site generator — pick the preset from the table in step 1. It fills in the other fields.
- Clean URLs — check against the table. If the value doesn’t match your generator, only the home page opens and every other page returns 404.
- Directory index — the
/aboutpage is stored asabout/index.html. - HTML extension — the
/aboutpage is stored asabout.html.
- Directory index — the
- 404 page —
404.html. - SPA mode — only for apps with in-browser routing (React Router, Vue Router). Turn it off for documentation, landing pages and business card sites: otherwise a wrong address returns the home page with status 200, and search engines index duplicates.
- Publish over HTTPS — normally on.
- Bot protection — leave it off at launch. For documentation and landing pages that need to show up in search, don’t enable Browser check unless you have to.
- Click Save settings.
5. Upload files and publish
Section titled “5. Upload files and publish”Through the panel
Section titled “Through the panel”Suits business card sites and landing pages.
- Files: drag files or a folder into the upload area. They are added to what’s already uploaded; matching paths are replaced.
- ZIP archive: Upload a ZIP archive → Choose ZIP. The archive fully replaces the existing version.
Zip the contents of the build folder, not the folder itself:
cd public && zip -r ../site.zip . && cd ..Review the file tree and click Publish.
With the CLI
Section titled “With the CLI”Suits automation: the CLI uploads only changed files and publishes the site.
curl -fsSL https://raw.githubusercontent.com/webshieldpro/webshield-cli/main/install.sh | shwebshield auth loginwebshield sites publish www.example.com --dir ./distCreate the login token under Settings in the panel. The --dry-run flag lists the changes without uploading.
From an S3 bucket
Section titled “From an S3 bucket”If the site is already in WebShield object storage, use the Publish from an S3 bucket block on the site card.
6. Verify
Section titled “6. Verify”-
Wait for the Published status.
-
Check the response:
Terminal window curl -I https://www.example.com/Expected:
HTTP/2 200. On the first publish the certificate takes a few minutes; if HTTPS doesn’t respond, try again later. -
Option B only: if you didn’t move the domain, switch the name — add this record at your DNS provider:
www.example.com. CNAME <address from the card>You’ll get an email once traffic flows through WebShield.
-
Open inner pages, images, search (for documentation) and any non-existent address in a browser — your 404 page should appear.
7. Set up auto-publishing
Section titled “7. Set up auto-publishing”Each publish creates a complete new version of the site: visitors never see an intermediate state, and the cache resets automatically.
- On the site card, in Deploy tokens (CI), create a token.
- Store it in your repository secrets as
WS_TOKEN. The token can only publish this site; revoke it in the same place. - Add a job: install the CLI and run one publish command. It uploads only the changed files and publishes a complete new version.
GitHub Actions, Gitea and Forgejo.
name: Deployon: push: branches: [main]
jobs: deploy: runs-on: ubuntu-latest env: WS_TOKEN: ${{ secrets.WS_TOKEN }} steps: - uses: actions/checkout@v5 - run: npm ci && npm run build # build → ./dist - name: Install the CLI run: curl -fsSL https://raw.githubusercontent.com/webshieldpro/webshield-cli/main/install.sh | sh - name: Publish run: ~/.local/bin/webshield sites publish www.example.com --dir ./distGitLab CI — add the token under Settings → CI/CD → Variables as WS_TOKEN, Masked and Protected:
deploy: image: node:22 rules: - if: $CI_COMMIT_BRANCH == "main" script: - npm ci && npm run build # build → ./dist - curl -fsSL https://raw.githubusercontent.com/webshieldpro/webshield-cli/main/install.sh | sh - ~/.local/bin/webshield sites publish www.example.com --dir ./distReplace the build command and folder with yours from the table in step 1. More detail — publishing from CI.
Add to your site
Section titled “Add to your site”| Task | For | Where to set up |
|---|---|---|
| Contact or lead form without your own server | landing page, business card | Forms on a static site |
| Redirect old addresses after a move | documentation, landing page | Redirects |
| Send some paths to your own server | landing page with an API | API on the same domain |
| Traffic and bot share | all | Statistics |
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause and fix |
|---|---|
| Creating the site fails with “Create an A or CNAME DNS record for this host first” | The name has no record in DNS. Create an A or CNAME (step 2). |
| The name is missing from the list or can’t be picked | Proxy is enabled for the name, or a site already exists. |
| 404 on every page, including the home page | index.html is not at the root: the archive was zipped together with its folder. |
| Home page opens, other pages return 404 | Wrong Clean URLs mode. Check how the page is stored in the build folder: about/index.html or about.html. |
| Styles and scripts don’t load | The build uses a base path or a local server address. Check base/baseURL/site_url in your generator’s settings. |
| A non-existent address opens the home page | SPA mode is on. Turn it off for documentation and landing pages. |
| HTTPS doesn’t work after 30 minutes | If the domain has CAA records, add letsencrypt.org. For option A, check that the status is Delegated. |
| Publishing fails with a line number | There’s an error in _redirects — fix the line shown. |
Upload rejects .exe, .apk and similar |
Executable files are available on commercial plans only. |
If your case isn’t listed, see Troubleshooting or contact support from the panel.