Skip to content
Issue docs

Form on an HTTPS page submitting over HTTP

Importantinsecure_form_actionIssue 126

What is this issue?

This check reports a form on an HTTPS page whose action submits to an http:// address.

The page arrives over an encrypted connection, so the padlock is shown and the visitor has every reason to trust it -- but everything they type into the form leaves in the clear.

The action is resolved against the page's own URL before it is judged, so the forms that are actually fine are not reported:

  • action="/subscribe" -- relative, inherits HTTPS.
  • no action attribute, or action="" -- submits to the page's own URL, HTTPS.
  • action="//forms.example.net/x" -- protocol-relative, inherits HTTPS.
  • action="mailto:..." -- not an HTTP submission at all.

On an HTTP page the check does not run. The whole document is already insecure there, and a separate check says so once rather than charging the same fact again for every form.

For a page to pass:

  • Every form on it resolves to an https:// (or non-HTTP) action.

Why it matters

An insecure form on a secure page is worse than an insecure page, because the visitor has been given a reason to trust it.

  • The data is readable in transit. Passwords, card numbers, addresses and messages cross the network unencrypted, and can be read or altered by anything between the visitor and the server.
  • Browsers interrupt the submission. Chrome and Firefox both block or warn on a mixed-content form submission. The form does not simply leak -- it frequently does not work, and the visitor sees a security warning attributed to your site.
  • The padlock is misleading. The page's own security indicator says encrypted; the submission is not.
  • It is usually one hard-coded URL left over from before an HTTPS migration.

Graded important: the page itself still serves, crawls and indexes, which is what keeps this out of critical, but the harm to a visitor who fills the form in is real and the browser warning costs conversions directly.

How to fix it

  1. Change the action to https://. In almost every case the destination already supports HTTPS and only the hard-coded URL is out of date.

  2. Prefer a relative action for forms that post back to your own site: action="/subscribe" inherits the page's scheme and cannot fall out of date again.

  3. Check third-party endpoints. Newsletter, CRM and payment providers all publish HTTPS endpoints. If one genuinely does not, that provider should be replaced, not worked around.

  4. Search the templates, not the pages. A form action is almost always in a shared partial, so one edit usually fixes every instance.

  5. Add a Content-Security-Policy form-action directive once the actions are correct, so a future regression is blocked by the browser rather than found by a crawler.

  6. Verify after the change. A browser console reports a mixed-content form submission explicitly.

Examples

Example 1: A relative action

Scenario: A newsletter sign-up posting back to the same site.

Passes because: the action resolves against the page URL and inherits HTTPS.

<form action="/subscribe" method="post">

Example 2: A hard-coded HTTP endpoint

Scenario: A contact form whose action was written before the site moved to HTTPS.

Fails because: everything typed into it is sent in the clear, and browsers will interrupt the submission with a warning.

<form action="http://example.com/contact-handler" method="post">

Corrected version:

<form action="/contact-handler" method="post">

Example 3: A third-party form endpoint

Scenario: A mailing-list provider embedded with the endpoint copied from old documentation.

Fails because: the subscriber's email address leaves the page unencrypted.

<form action="http://lists.provider.example/subscribe" method="post">

Corrected version: use the provider's HTTPS endpoint.

<form action="https://lists.provider.example/subscribe" method="post">

How PixyScan detects this

  1. Checks the page is SERVED over HTTPS. The address it judges is the one the page resolved to, not the one that was requested, so a page linked over http:// that redirects to https:// is correctly treated as secure and its forms are still checked. On a page that is genuinely still served over HTTP the check does not run at all -- the document is already insecure, and that is reported once by its own check rather than repeated per form.

  2. Finds every form with an action attribute.

  3. Resolves each action against the page's own URL, exactly as a browser would. This is what makes a relative action, an empty action and a protocol-relative action all resolve to HTTPS and correctly pass.

  4. Reports the ones that resolve to http://. The scheme comparison ignores case, so HTTP:// is caught too, and the address is recorded in its normalised absolute form.

  5. Produces one finding for the page, listing every insecure action it found and how many there were, rather than a row per form.

A form with no action, or an empty one, is not reported: it submits to the page's own address, which is HTTPS.

What we store

Storage Level

Page Level


Database Table / Prisma Model

audit_issues.details


Stored Fields

Field Type Description
insecureFormActions String[] Each insecure action, resolved to an absolute URL
insecureFormCount Int How many forms on the page submit over HTTP

Detection Dependencies

  • HTML Document
  • The page URL as the crawler fetched it

Note

Kept on the finding. The actions are stored RESOLVED rather than as written, because a relative action tells the reader nothing about where the data goes.

Further reading