Add a Contact Form to GitHub Pages (No Server Needed)
GitHub Pages can't run server code, but your contact form can still email you. The copy-paste setup that works on plain HTML, Jekyll, and Hugo sites — spam filtering included.
The ShipMyForm team
· 2 min read
GitHub Pages cannot process forms — and it never will. Pages serves static files, full stop: no PHP, no Node, no serverless functions. That's not a limitation to work around with hacks; it's the deal that makes Pages free and fast. A working contact form on Pages therefore needs exactly one thing: a URL somewhere else to POST to.
That URL is a form endpoint, and wiring it up is the whole tutorial:
<form action="https://shipmyform.com/f/YOUR_FORM_ID" method="POST">
<input type="text" name="name" placeholder="Your name" required />
<input type="email" name="email" placeholder="[email protected]" required />
<textarea name="message" placeholder="Say hello" required></textarea>
<!-- honeypot: bots fill it, humans never see it -->
<input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none" />
<!-- send visitors back to your site afterward -->
<input type="hidden" name="_redirect" value="https://yourname.github.io/thanks/" />
<button type="submit">Send</button>
</form>Commit, push, done. Each field's name becomes a field in the email you
receive; the endpoint stores a copy in a searchable inbox as backup. As of
2026, ShipMyForm's free plan (100 submissions/month, spam filtering included,
no credit card) covers the traffic of almost any personal or project site on
Pages.
Why the "easy" alternatives fail on Pages
mailto:links don't send anything — they ask the visitor's machine to open a mail app and compose a draft. No configured mail client (most people) means a dead button. And your address is now scraped from both your site and your public repo. The mechanics of why this can't work are in sending form email without SMTP.- A PHP
contact.php— the classic tutorial answer — simply doesn't execute. Pages serves the file as plain text, source code and all. - Client-side email APIs run, but ship their sending token in your bundle — in a public repository. Anyone can lift it and burn your quota within minutes of finding it.
Jekyll, Hugo, and friends
Pages sites are usually generated, and the pattern doesn't care. In Jekyll,
drop the form into a page, layout, or _includes/contact.html — front matter
and Markdown pages accept raw HTML as-is. For Hugo (commonly deployed to
Pages via Actions), the same markup goes in a partial —
the Hugo guide has the full walkthrough. Same
for Docusaurus, mdBook, MkDocs: if it emits HTML, the form works.
Custom domain instead of github.io? Nothing changes — the endpoint accepts
your form wherever the page is served, and you can restrict it to your domains
so nobody reuses your endpoint on their site.
A form on a public site gets found by bots — usually within days. Since Pages can't run filtering for you, choose an endpoint that does it server-side: honeypots, rate limiting, and ML classification without making humans solve puzzles. Blocked spam shouldn't count against your quota, either.
The bigger picture
This split — static site on Pages, form handling on a form backend — is the standard architecture, not a compromise. Your site stays free to host, versioned in git, and deployable by push; the form gets storage, spam defense, and delivery to email (or Slack and Google Sheets) that a static host could never provide.
Create a free endpoint, paste it into your form's action, push, and
your GitHub Pages site has a working contact form before the Actions run
finishes.
Frequently asked questions
- Can GitHub Pages handle form submissions?
- Not by itself — GitHub Pages serves static files only and will never run PHP, Node, or any server code. A form on a GitHub Pages site works by POSTing to an external form endpoint, which stores the submission and emails it to you. That's a one-attribute change in your HTML.
- How do I get contact form emails from my github.io site?
- Point the form's action attribute at a form backend endpoint. The backend receives each submission over HTTPS, filters spam, and delivers it to your inbox from an authenticated sending domain. ShipMyForm's free plan covers 100 submissions a month, which fits most GitHub Pages sites comfortably.
- Does this work with Jekyll?
- Yes — Jekyll outputs static HTML, so the same form markup works whether you write it in a layout, an include, or a Markdown page with HTML in it. Nothing about the pattern is Jekyll-specific; it also works on Hugo, mdBook, Docusaurus, or hand-written HTML on Pages.
- Why not just use a mailto: link?
- mailto doesn't submit anything — it tries to open the visitor's mail app with a draft they still have to send. On machines without a configured mail client (most of them), nothing happens, and your email address is exposed to every scraper reading your repo and site. A form endpoint receives the data reliably and keeps your address private.
- Will spam be a problem on a public GitHub Pages form?
- Every public form gets found by bots, and since you can't run server code on Pages, the filtering has to live at the endpoint. A good form backend screens submissions with honeypots, rate limiting, and machine-learning classification before anything reaches your inbox — no CAPTCHA for real visitors.
Related guides
Hugo Contact Form Without a Backend (2026)
A copy-paste contact form for Hugo that emails you on every submission — drop it in as a shortcode, no server needed.
Send Email From an HTML Form — No SMTP, No PHP
What SMTP actually is, why your HTML form can't speak it (and shouldn't), and the modern setup that emails you submissions with zero mail servers.
What Is a Form Backend? (And When You Need One)
The plain-language definition, how form endpoints work, and when a hosted backend beats rolling your own.