Skip to content
FormSubmit

HTML

HTML form action attribute explained (with method, enctype and examples)

How the HTML form action attribute works: method GET vs POST, enctype, target, formaction, what happens on submit, and where action should point.

7 min readUpdated Oct 8, 2026

The HTML form action attribute is the URL the browser sends a form's data to when it's submitted. Together with method (how to send it, GET or POST) and enctype (how to encode it), it decides what happens when someone presses submit. If action is left out, the form submits to the page it's on. If you don't have a server to receive the data, point action at a hosted form endpoint and the data is stored and emailed to you.

This guide covers every attribute that controls form submission, what the browser actually does, and the mistakes that cause most "my form doesn't work" questions.

A minimal form

minimal-form.html
<form action="/subscribe" method="POST">
  <label for="email">Email</label>
  <input id="email" type="email" name="email" required>
  <button type="submit">Subscribe</button>
</form>

When submitted, the browser sends a POST request to /subscribe on the same site, with the body email=ada%40example.com. Whatever lives at /subscribe has to read that data and respond.

The HTML form action attribute

action takes a URL. That URL can take several forms.

Absolute URLs

html
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST">

The data goes to exactly that address, even on a different domain. Posting a plain HTML form to another site is normal and isn't blocked by the browser. CORS only applies when JavaScript reads the response, which a normal form submission doesn't do.

Relative URLs

html
<form action="/contact" method="POST">        <!-- root-relative: https://yoursite.com/contact -->
<form action="contact.php" method="POST">     <!-- relative to the current page's folder -->
<form action="?step=2" method="POST">         <!-- same page, new query string -->

Relative URLs resolve against the current page, or against a <base href> if the page has one. Root-relative paths that start with / are the least surprising.

Empty or missing action

html
<form method="POST">
<form action="" method="POST">

Both submit to the current page's URL. That's handy when the same server-side script renders and processes the form. On a static site, though, it posts to an HTML file that can't do anything with the data. Most static hosts return an error or simply reload the page, which is a common reason a form "does nothing".

mailto: actions

action="mailto:you@example.com" opens the visitor's email app instead of sending anything. It's unreliable, and send an HTML form to email explains why and what to do instead.

The method attribute: GET vs POST

method controls how the data travels. Only two values matter for HTML forms: get (the default) and post. A third value, dialog, closes a surrounding <dialog> without sending a request.

GETPOST
Where the data goesIn the URL: /search?q=shoes&size=9In the request body
Visible in history, logs, analyticsYesNo
Bookmarkable and shareableYesNo
Size limitPractical URL limits (a few thousand characters)Large bodies are fine
FilesCan't send filesSupported with multipart/form-data
Use forSearch, filters, paginationContact forms, sign-ups, logins, uploads, payments

The rule of thumb: if submitting changes something or contains personal data, use POST. A login form with method="get" puts the password in the address bar, the browser history and the server's access logs.

search-form.html
<!-- Good use of GET: the results page is bookmarkable -->
<form action="/search" method="GET">
  <input type="search" name="q" aria-label="Search">
  <button type="submit">Search</button>
</form>

Method names aren't case sensitive, so post, POST and Post all work.

The enctype attribute

enctype sets how a POST body is encoded. It's ignored for GET.

ValueWhen to use it
application/x-www-form-urlencodedThe default. Fine for text fields.
multipart/form-dataRequired for file inputs. Without it, only the file name is sent.
text/plainRarely useful. Hard for servers to parse reliably.
upload-form.html
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST" enctype="multipart/form-data">
  <input type="email" name="email" required>
  <input type="file" name="resume" accept=".pdf">
  <button type="submit">Apply</button>
</form>

The file upload form guide covers multiple files and size limits.

The target attribute

target chooses where the response loads, just like on a link:

html
<form action="/report" method="POST" target="_blank">

_self is the default. _blank opens a new tab, and the name of an <iframe> loads the response there. It's occasionally useful for downloads or previews, but most forms should leave it alone.

Other submission attributes

  • accept-charset sets the character encoding. Modern pages use UTF-8, which is the default, so you rarely need it.
  • autocomplete="off" asks the browser not to autofill fields. Browsers often ignore it for login fields.
  • novalidate turns off the browser's built-in validation (required, type="email" and so on). Use it only if you validate with your own JavaScript.
  • name on the form itself has no effect on submission. It's just a label for scripts.

formaction and friends on buttons

A submit button can override the form's attributes for that one submission:

formaction.html
<form action="/posts" method="POST">
  <input type="text" name="title" required>
  <textarea name="body"></textarea>

  <button type="submit">Publish</button>
  <button type="submit" formaction="/posts/draft">Save draft</button>
  <button type="submit" formaction="/preview" formtarget="_blank" formnovalidate>Preview</button>
</form>

formaction, formmethod, formenctype, formtarget and formnovalidate mirror the form-level attributes. A button with a name and value is also submitted, which is another way to tell which button was pressed.

What the browser does when a form is submitted

Pressing a submit button, or pressing Enter in a text field, starts this sequence:

  1. Validation. Built-in constraints such as required, type="email", minlength and pattern are checked. If any fail, the browser shows a message and stops, unless the form or button has novalidate.
  2. The submit event fires. JavaScript can call event.preventDefault() here to stop the normal submission and send the data itself. See submit a form with JavaScript.
  3. The data is collected. Every field that has a name, isn't disabled, and (for checkboxes and radio buttons) is checked becomes a name/value pair. Fields without a name are skipped.
  4. The data is encoded according to method and enctype.
  5. The request is sent to action, and the browser navigates to the response, just like following a link.

That last step is why a plain form "leaves the page". Whatever the server returns, whether a thank-you page, an error or a redirect, replaces the current page. Well-built backends answer a POST with a redirect (303 See Other) so that refreshing the thank-you page doesn't submit the form again.

Common mistakes

Inputs without a name

html
<!-- Not submitted: no name -->
<input type="email" id="email">

<!-- Submitted as email=... -->
<input type="email" id="email" name="email">

id is for labels, CSS and scripts. name is what gets submitted. This is the single most common reason a field is missing from the data.

Sensitive data with GET

Passwords, emails and messages in the URL end up in browser history, server logs and analytics tools. Use method="POST".

File inputs without multipart

If a file input arrives as a plain file name, add enctype="multipart/form-data".

Disabled fields

disabled inputs aren't submitted. If a value should be sent but not edited, use readonly or a hidden input.

Nested forms

A <form> inside another <form> isn't allowed. Browsers ignore the inner form tag, which leads to confusing results. To link an input outside the form, use the form attribute: <input name="note" form="checkout">.

Pointing action at a static file

On GitHub Pages, Netlify or any static host, action="contact.php" or action="/submit" does nothing useful, because there's no code there to run.

Where should the form action point if you have no server?

This is the question most people actually have. A form needs something at the action URL that can receive a POST, store or forward the data, and respond. If your site is static, or you just don't want to write and maintain backend code, use a hosted form endpoint.

With FormSubmit, sign up with Google or email, create a form, and use its endpoint as the action:

contact.html
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST">
  <div>
    <label for="fs-name">Name</label>
    <input id="fs-name" type="text" name="name" placeholder="Jane Doe" required>
  </div>
  <div>
    <label for="fs-email">Email</label>
    <input id="fs-email" type="email" name="email" placeholder="jane@example.com" required>
  </div>
  <div>
    <label for="fs-message">Message</label>
    <textarea id="fs-message" name="message" rows="5" placeholder="How can we help?" required></textarea>
  </div>
  <!-- Honeypot: leave this hidden field empty to catch bots -->
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <!-- Optional: where to send people after submitting -->
  <!-- <input type="hidden" name="_redirect" value="https://yoursite.com/thanks"> -->
  <button type="submit">Send</button>
</form>

FormSubmit accepts URL-encoded, multipart and JSON bodies. Each submission is stored in your dashboard and emailed to you, with Reply-To set to the visitor. Spam is filtered out, and the visitor is redirected with a 303 to a thank-you page (yours, if you set one). Hidden control fields such as _subject and _redirect adjust that behaviour, and the endpoint reference lists every response and error code. The free plan includes one form and 50 submissions a month.

To customise the fields, build the form in the form generator. For a complete contact form for website projects, there's a ready-made page too. If you're replacing an old PHP handler, contact form without PHP shows how to swap the action and keep your existing field names.

Quick reference

AttributeDefaultWhat it controlsButton override
actionCurrent page URLWhere the data is sentformaction
methodgetget (in the URL) or post (in the body)formmethod
enctypeapplication/x-www-form-urlencodedBody encoding; multipart/form-data for filesformenctype
target_selfWhere the response loadsformtarget
novalidateOffSkips built-in validationformnovalidate

Frequently asked questions

What does the action attribute do in an HTML form?

It sets the URL the browser sends the form data to when the form is submitted. If it's missing or empty, the browser submits to the current page's URL.

Should I use GET or POST?

Use GET for searches and filters where the result should be bookmarkable. Use POST for contact forms, sign-ups, logins, uploads and anything with personal data, because GET puts every value in the URL.

Why is my form field not being submitted?

Almost always because the input has no name attribute. The browser only submits fields that have a name and aren't disabled.

Can the action be a different website?

Yes. The action can be any absolute URL, including a form backend on another domain. A plain HTML form post isn't blocked by CORS; CORS only matters when you submit with JavaScript.

Where should action point if I don't have a server?

Point it at a hosted form endpoint. With FormSubmit, use https://formsubmit.app/f/YOUR_FORM_ID and method POST, and submissions are stored and emailed to you.

Keep reading

Your form backend is 60 seconds away

Sign up with Google or email, create a form, paste the endpoint. Free forever for small sites — no credit card.