Modern best practices in both React and Angular make a dangerous omission: the method
attribute on form elements. This can lead to accidental logging of user credentials.

It is now considered standard to hijack a form
’s submit
behavior in these frameworks using onSubmit
or ng-submit
, and most code examples don’t even bother to give their form element a method
. This is because the JS function that hijacks the submit behavior takes care of the form’s job using an XHR request; the form
element is mostly just there for semantic reasons.
This is documented best practice for the top two JS frameworks:

Here are high-search-ranking tutorials showing how to make a login form in React. They all use forms with no method
, which have password
inputs.
- https://serverless-stack.com/chapters/create-a-login-page.html
- https://scotch.io/tutorials/validating-a-login-form-with-react
Here’s a similar tutorial for Angular:
The Problem
As the W3 spec will tell you, the default value for method is GET
, not POST
. Over the years it’s become clear that this is usually not desired behavior for an HTML form, but we are stuck with this default.
That’s a problem when combined with these so-called best practices, because server-side rendering for React and Angular is becoming easier and more common in recent years thanks to tools like Next.JS and Angular Universal. This means that there’s a lot of code out there written under the assumption that the login form wouldn’t exist if the client-side JavaScript had thrown some exception and failed to run.
Adding server-side rendering to this existing code breaks that assumption; the server might render your components just fine while the client-side script doesn’t run or throws an exception. If you omit method
you’ll wind up serving a GET
form to users and logging their passwords when any of the following is true:
- The script throws an exception before the
submit
override can bind to the form - The script throws an error inside the
submit
override, before the override can calle.preventDefault()
- You have a visitor with JavaScript disabled.
We discovered this by chance when reviewing some changes to the Cryptowatch login UI – our React components rendered fine in the Next.JS server but JavaScript threw an exception locally due to a failed network request. When I clicked “Log in” the page just refreshed with my username and password printed in clear text in the URL bar as query parameters.
The Solution
Avoiding this is simple: just don’t omit method
! Make sure the form still submits as a POST
as a regular old form, with no JavaScript enabled.
It would also be nice if web browsers intercepted GET
forms that contain a password
field and show a warning dialog to the end user, similar to how they do with unencrypted HTTP connections. Maybe someone on the Chrome team is reading? 🙂
Are you a security conscious web developer? We’re hiring! https://cryptowat.ch/careers