# The HTML form action attribute, explained

> What a form's action attribute does, how relative and empty values resolve, how formaction sends one form to two places, and why static hosts answer 405.

Published 2026-10-25 · https://ikiform.com/guides/html-form-submission/action-attribute

The `action` attribute is the URL a form sends its data to. A relative value resolves against the page's address, an empty or missing one means the page itself, and a submit button's `formaction` overrides it for that button. On a static site, set it to the full URL of a form backend such as `https://ikiform.com/f/<formId>`, because your own host can't take the POST.

Most broken forms on static sites are broken in this one attribute. The markup looks fine, the button works, and the browser shows a `405` or a blank page, because the data went somewhere that can't receive it. This page shows exactly where a given `action` sends data, then uses `formaction` to route one form to two inboxes.

## What's different here

`action` is a URL like any `href`, with one twist: a form with no `action` doesn't do nothing, it posts to the page it's on. On a server-rendered site that page might handle the post. On a static host it can't, so a missing or relative `action` fails only after you deploy.

## Where each value sends the data

Take a page at `https://example.com/company/contact.html` and five forms on it:

`contact.html`:

```html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
<form action="/api/contact" method="post">
<form action="send" method="post">
<form action="" method="post">
<form method="post">
```

| action | Resolves to | On a static host |
| --- | --- | --- |
| `https://ikiform.com/f/frm_...` | Exactly that URL | Works. Ikiform takes the POST |
| `/api/contact` | `https://example.com/api/contact` | Only works if your host runs a function at that path |
| `send` | `https://example.com/company/send`, relative to the folder | `405` or `404` |
| `""` (empty) | The page itself, `contact.html` | `405` on most static hosts |
| Missing | The page itself | `405` on most static hosts |

Diagram: How the browser turns a form's action attribute into the URL it posts to

1. An absolute action is used exactly as written, so the post reaches Ikiform, which answers with a redirect.
2. A relative, empty or missing action is resolved against the page's own address, or against a base element if the page has one.
3. The post then goes to your own host. A static host serves files only, so it refuses the POST, usually with 405.

If the address bar shows your own domain right after submitting, the action was relative.

> A `<base href>` element in the page's head changes how every relative URL resolves, including `action`. Some site builders and single-page app setups add one. Use a full URL and it can't matter.

### Check what the browser will use

In the browser console, the attribute and the property differ. `getAttribute` returns what you wrote; the `action` property returns the resolved URL the browser will actually post to:

`Console`:

```js
const form = document.querySelector("form");
form.getAttribute("action");
form.action;
```

If `form.action` shows your own domain, that's where the data goes. Builders such as Webflow and Framer often set `action` from their own settings panel, so check the published page, not your source. The [site builder guides](https://ikiform.com/docs/guides/site-builders.md) say where each builder keeps it.

## One form, two inboxes

A submit button's `formaction` replaces the form's `action` for that button only. Here, one set of fields goes to two Ikiform forms. **Ask support** posts to the support form, **Talk to sales** to the sales form, and each Ikiform form emails its own recipients:

`contact.html`:

```html
<form action="https://ikiform.com/f/frm_supportxxxxxxxxx" method="post">
  <label for="email">Email</label>
  <input id="email" name="email" type="email" autocomplete="email" required />

  <label for="message">How can we help?</label>
  <textarea id="message" name="message" rows="5" required></textarea>

  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Ask support</button>
  <button formaction="https://ikiform.com/f/frm_salesxxxxxxxxxxx">Talk to sales</button>
</form>
```

- Each Ikiform form has its own notification recipients, spam settings and webhooks, so support and sales never see each other's mail.
- Both forms need the same field definition, or none. With fields defined, a field one form doesn't know is refused with `422`.
- The other `form*` attributes work the same way: `formmethod`, `formenctype`, `formnovalidate` and `formtarget`. A **Save draft** button with `formnovalidate` skips the browser's checks.
- `formaction` only works on submit buttons: `<button>`, `<input type="submit">` and `<input type="image">`.

The alternative is one Ikiform form with a `topic` field, and routing by topic in a webhook or your inbox filters. That keeps one list and one export. Two forms are simpler when the two teams shouldn't share recipients at all.

## action with a query string

With `method="get"`, the browser replaces the action's query string with the form's fields. The `lang=de` in the first form below is silently dropped; the second keeps it as a hidden input:

`search.html`:

```html
<form action="/search?lang=de" method="get">
  <input name="q" />
</form>

<form action="/search" method="get">
  <input name="q" />
  <input type="hidden" name="lang" value="de" />
</form>
```

With `method="post"` the query string is kept, because the fields go in the body. That doesn't change anything for Ikiform: query parameters on the endpoint URL are not stored, so use hidden inputs for values you want kept.

## Inputs outside the form

Layouts sometimes need an input that isn't inside the `<form>` element, such as a footer signup next to other content. The `form` attribute on an input or button joins it to a form by `id`, and it posts to that form's `action`:

`footer.html`:

```html
<form id="newsletter" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post"></form>

<footer>
  <input form="newsletter" name="email" type="email" required />
  <button form="newsletter">Subscribe</button>
</footer>
```

## mailto: and javascript: actions

`action="mailto:you@example.com"` asks the visitor's mail app to open a message with the fields. Nothing is sent unless they have a mail app set up and press send, and you never learn that they tried. `action="javascript:..."` runs code instead of submitting and is blocked by most content security policies. Both are worth replacing with a real endpoint.

## Check it works

1. Under the form's **Allowed domains**, add your site's domain, plus `localhost` while you develop. Leave it empty to accept posts from anywhere.
2. Open the published page, not your source, and run `document.querySelector("form").action` in the console. It prints your full Ikiform URL.
3. Submit the form. The Network panel shows a POST to `ikiform.com` answered with `303`, not a POST to your own domain.
4. In the two-inbox form, submit once with each button. Each submission appears under its own form in the dashboard.
5. Check the inbox of each notification recipient. Each gets an email with the fields, and **Reply** goes to the submitter when the form has an `email` field.

## Gotchas

| Problem | Cause and fix |
| --- | --- |
| `405 Method Not Allowed` from your own domain | The action is relative, empty or missing, so the post went to your static host. Use the full Ikiform URL |
| `404 not_found` from ikiform.com | The form id in the action is wrong, or the form was deleted. Copy the endpoint from the form's page in the dashboard |
| The builder keeps resetting the action | Some builders own the `action` of their form block. Use their custom action setting, or an embed with your own markup |
| The browser warns that the form is not secure | The action uses `http:` on an `https:` page. Ikiform's endpoint is `https:` only; fix the scheme |
| Fields from a second form are posted too | Forms can't be nested. The browser closes the outer form at the inner `<form>` tag. Use the `form` attribute instead |

For the full submission flow, from the click to the redirect, see the [overview](https://ikiform.com/guides/html-form-submission.md#how-it-works). Everything the endpoint accepts is in [HTML forms](https://ikiform.com/docs/html.md).

## Questions

### What happens if a form has no action attribute?

It submits to the page it's on. That works when your server handles posts at that URL, but on a static host it fails, usually with 405 Method Not Allowed.

### What is the difference between action and formaction?

action is set on the form and applies to every submit. formaction is set on one submit button and overrides action when that button is used, so one form can send to two places.

### Can the form action be a URL on another domain?

Yes. Browsers post cross-site with no special setup, which is how a static page posts to a form backend. The receiving server decides whether to accept it; Ikiform checks the page's origin against the form's allowed domains.

### Why do my query string parameters disappear from the action URL?

With method get, the browser replaces the action's query string with the form's own fields. Move each parameter into a hidden input. With method post the query string is kept.

## More guides

- [HTML form submission](https://ikiform.com/guides/html-form-submission.md): What a browser does when a form is submitted: which fields it sends, how it encodes them, what the server should answer, and how Ikiform answers.
- [Redirect after submit](https://ikiform.com/guides/html-form-submission/redirect-after-submit.md): Send visitors to your own thank-you page after a form posts: the 303 redirect, the _redirect field, which domains are allowed and what refresh does.
- [Without a page reload](https://ikiform.com/guides/html-form-submission/without-page-reload.md): Send a form with fetch and FormData so the visitor stays on the page, with field errors, a loading state, retries and a fallback when scripts fail.
- [Form validation](https://ikiform.com/guides/html-form-submission/validation.md): Validate a form with required, type, pattern and min, style errors with :user-invalid, and enforce the same rules on the server with field definitions.
