Persistent 400 Bad Request when Replicating Web Login POST with HTTP Request Node

Hey everyone,

I’m trying to automate logging into a website (signin.crmls.org) using the HTTP Request node in n8n (v1.90.2 Cloud). The goal is to perform a POST request to the login endpoint, handle redirects, and establish an authenticated session for subsequent steps (like searching for records).

I’ve analyzed the successful browser login request using developer tools and have a HAR log. It shows a standard POST request with Content-Type: application/x-www-form-urlencoded, including form parameters like Username, Password, and a dynamic __RequestVerificationToken. The successful flow involves a few 302 redirects.

My n8n workflow currently does the following:

  1. HTTP Request (GET Login Page): Fetches the initial login page to get the HTML.

  2. HTML Extract: Parses the HTML to extract the __RequestVerificationToken value from a hidden input field.

  3. HTTP Request (POST Login): Attempts to send the login credentials and the extracted token.

However, this POST request consistently results in a 400 Bad Request error from the server.

Here is the relevant request data from my browser’s HAR log for the successful POST login:

{
  "method": "POST",
  "url": "https://signin.crmls.org/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3DmemberPortalUI%26redirect_uri%3Dhttps%253A%252F%252Fmember.recenterhub.com%252Fcallback%26response_type%3Dcode%26scope%3DODataApi%2520openid%2520CrmlsProfile%2520MemberPortalApi%2520offline_access%26state%3Df4e7a2625fc949108fb119e54c11c092%26code_challenge%3DsyCjb-UqfUFShJmkLmMNTKuIsM1TTY0YHN-SUwshju4%26code_challenge_method%3DS256%26response_mode%3Dquery",
  "httpVersion": "http/2.0",
  "headers": [
    { "name": "accept", "value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7" },
    { "name": "accept-encoding", "value": "gzip, deflate, br, zstd" },
    { "name": "accept-language", "value": "en-US,en;q=0.9" },
    { "name": "cache-control", "value": "no-cache" },
    { "name": "content-type", "value": "application/x-www-form-urlencoded" },
    { "name": "origin", "value": "null" },
    { "name": "pragma", "value": "no-cache" },
    { "name": "priority", "value": "u=0, i" },
    { "name": "referer", "value": "https://signin.crmls.org/Account/Login?ReturnUrl=..." /* Full ReturnUrl from GET page URL */},
    { "name": "sec-ch-ua", "value": "\"Not(A:Brand\";v=\"99\", \"Opera\";v=\"118\", \"Chromium\";v=\"133\"" },
    { "name": "sec-ch-ua-mobile", "value": "?0" },
    { "name": "sec-ch-ua-platform", "value": "\"Windows\"" },
    { "name": "sec-fetch-dest", "value": "document" },
    { "name": "sec-fetch-mode", "value": "navigate" },
    { "name": "sec-fetch-site", "value": "same-origin" },
    { "name": "sec-fetch-user", "value": "?1" },
    { "name": "upgrade-insecure-requests", "value": "1" },
    { "name": "user-agent", "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 OPR/118.0.0.0" }
  ],
  "postData": {
    "mimeType": "application/x-www-form-urlencoded",
    "text": "ReturnUrl=/* value */&LayoutLogo=/* value */&LoginLogo=/* value */&Username=[YOUR_ACTUAL_USERNAME]&Password=[YOUR_ACTUAL_PASSWORD]&button=login&__RequestVerificationToken=[THE_TOKEN_VALUE]",
    "params": [
      { "name": "ReturnUrl", "value": "/* value */" }, // Note: ReturnUrl appears twice in the raw text, but only once here. Add it twice in n8n Body Parameters.
      { "name": "LayoutLogo", "value": "/* value */" },
      { "name": "LoginLogo", "value": "/* value */" },
      { "name": "Username", "value": "[YOUR_ACTUAL_USERNAME]" },
      { "name": "Password", "value": "[YOUR_ACTUAL_PASSWORD]" },
      { "name": "button", "value": "login" },
      { "name": "__RequestVerificationToken", "value": "[THE_TOKEN_VALUE]" }
    ]
  }
}

(Note: I’ve replaced sensitive data and some long values with placeholders like /* value */ or [YOUR_ACTUAL_USERNAME] to keep the post concise and secure. In n8n I’m using literal values or expressions for testing).

In my n8n HTTP Request node configuration for the POST login, I have:

  • Method: POST

  • URL: The full URL from the HAR

  • Body Content Type: Form Urlencoded

  • Body Parameters: Added all the listed parameters (ReturnUrl twice, LayoutLogo, LoginLogo, Username, Password, button, __RequestVerificationToken) with values matching the HAR (Username/Password are literal for testing, Token is from the previous HTML Extract node).

  • Headers: Added all the headers listed in the HAR (Accept, Accept-Language, Cache-Control, Origin, Pragma, Referer, sec-ch-*, Upgrade-Insecure-Requests, User-Agent). I did not manually add Content-Type as selecting “Form Urlencoded” should handle this.

However, the node fails with this error:

{
  "errorMessage": "Bad request - please check your parameters",
  "errorDescription": "400 - \"\"",
  "errorDetails": {
    "rawErrorMessage": [
      "400 - \"\""
    ],
    "httpCode": "400"
  }
}

IGNORE_WHEN_COPYING_START

content_copy download

Use code with caution. Json

IGNORE_WHEN_COPYING_END

(I’ve removed the n8nDetails and stackTrace to keep the post short and within limits).

I’ve checked the “Request” tab in the n8n node’s output after execution, and it appears to be sending a request very similar to the one in the HAR, specifically:

Ok, I wanted to add the execution request history here, but when I go to the execution and click on the http node there is no request tab!!!! So I cannot copy and paste the http node or the request it makes! If you know how I can get that I appreciate it!!

Despite the apparent similarity, the server rejects it.

My questions are:

1. Given the detailed HAR comparison and the persistent 400, are there any other common pitfalls or subtle differences I might be missing when replicating a browser POST login?

2. Is there a way to get an even more raw view of the request sent by the n8n HTTP Request node, including the exact byte stream, to compare it definitively against the HAR's raw postData.text?

3. Could this indicate the website is using client-side JavaScript checks or other browser-specific validation that makes the HTTP Request approach infeasible?

Any insights or debugging tips would be greatly appreciated! 

Thank you!

Re: Question #2 - Is there a way to get… raw view…?

Yes. You’ll need to duplicate the n8n environment (same version) locally in docker, and then set up mitmproxy to capture the request/response “on the wire.” This won’t account for differences between the n8n-cloud environment and a local environment but it might help you spot differences in the way n8n sends the request. You could potentially also configure your browser to use mitmproxy and catch the request/response from that too.

I have to run an entire local docker server just to see the log of the http request? That basically makes development on the n8n cloud so limited and impractical if you cannot see what request is being sent.

So, you are trying to hack through the details of a non-browser (non-standard) interaction with a front-end web app, using a tool (n8n) that is meant for interactions with well defined back-end web services, and you have a problem with the “inconvenience” of the available analysis tools/techniques (mitmproxy, docker, etc)? :rofl: :joy:

1 Like

You can view the http requests in the browser console but what I tend to do is fire off a request to webhook.site to see what is being sent.

When trying to log into a site that doesn’t have an api attempted the request with curl is always worth doing to see if that can make it, it could be a header causing an issue.

1 Like