HttpClient does not set X-XSRF-Token on Http Post · Issue #20511 · angular/angular · GitHub
Skip to content

HttpClient does not set X-XSRF-Token on Http Post #20511

Closed

Description

@justin-ruffin

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[ X] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

Updated to angular 5.0.2. Updated from the deprecated HttpModule (@angular/http) to HttpClientModule (@angular/common/http).

Updated Http.post to HttpClient.post and tested. X-XSRF-TOKEN was not present in the Http Header.

HttpClient (@angular/common/http) does not set X-XSRF-Token on Http Post, while Http (@angular/http) does.

Expected behavior

HttpClient should set the X-XSRF-TOKEN on Http Post.

Minimal reproduction of the problem with instructions

Verify that javascript XSRF-TOKEN cookie has been set.

Test HttpClient (@angular/http) and HttpClientModule (@angular/common/http) side by side with nearly identical Http post requests.


//OLD
        CreateOld(sample: Models.Sample): Observable {
            let body = JSON.stringify(sample);
            let headers = new Headers({ 'Content-Type': 'application/json' });
            return this.http
                .post(this.baseUrl + 'api/sample/Create', body, { headers: headers })
                .map(response => { return response.json() as Models.Task[] });
        }
//NEW
	 CreateNew(sample: Models.Sample): Observable {
			let body = JSON.stringify(sample)
			return this.httpClient
				.post(this.baseUrl + 'api/sample/Create', body, { headers: new HttpHeaders().set('Content-Type', 'application/json') });
        }

What is the motivation / use case for changing the behavior?

Environment


Angular version: 5.0.2


Browser:
- [ X] Chrome (desktop) version 62.0.3202.94 
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [X ] Edge version 41.16299.15.0
 
For Tooling issues:
- Node version: XX  
- Platform:  

Others:

Activity

  1. Toxicable commented on Nov 18, 2017

    @Toxicable
  2. justin-ruffin commented on Nov 18, 2017

    @justin-ruffin
    Author

    Yes, we already have that implemented. Still an issue.

    Per your documentation on HttpClientModule, please see the section in bold. This is either a bug or a invalid documentation.

    "Security: XSRF Protection

    Cross-Site Request Forgery (XSRF) is an attack technique by which the attacker can trick an authenticated user into unknowingly executing actions on your website. HttpClient supports a common mechanism used to prevent XSRF attacks. When performing HTTP requests, an interceptor reads a token from a cookie, by default XSRF-TOKEN, and sets it as an HTTP header, X-XSRF-TOKEN. Since only code that runs on your domain could read the cookie, the backend can be certain that the HTTP request came from your client application and not an attacker.

    By default, an interceptor sends this cookie on all mutating requests (POST, etc.) to relative URLs but not on GET/HEAD requests or on requests with an absolute URL.

    To take advantage of this, your server needs to set a token in a JavaScript readable session cookie called XSRF-TOKEN on either"

    I have found another bug report #18859, "HttpClient HttpXsrfInterceptor does not set xsrf token for absolute urls" which is a duplicate of this issue.

  3. sajagekar commented on Nov 20, 2017

    @sajagekar

    I am also facing the same issue with angular 4. I can see the cookie in the response headers but while posting data angular is not adding appropriate headers , also I have tried with HttpClientXsrfModule.

  4. added this to the Backlog milestone on Jan 23, 2018
  5. ewaschenko commented on Feb 9, 2018

    @ewaschenko

    Any update? I have my XSRF-TOKEN non-HttpOnly and angular still does not set X-XSRF-TOKEN

  6. modified the milestones: Backlog, needsTriage on Feb 26, 2018
  7. bhanukumar04 commented on Mar 9, 2018

    @bhanukumar04

    am also facing the same issue with Angular 5.2.X version, is there any work around for it. In the first request am getting XSRF-TOKEN cookie in response and in the next requests am expecting to add cookie by angular which is not happening and my XSRF-TOKEN non-HttpOnly.

  8. ewaschenko commented on Mar 9, 2018

    @ewaschenko

    @bhanukumar04 I just created an Interceptor and did it myself.

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
            let requestToForward = req;
            let token = this.tokenExtractor.getToken() as string;
            if (token !== null) {
                requestToForward = req.clone({ setHeaders: { "X-XSRF-TOKEN": token } });
            }
            return next.handle(requestToForward);
        }
    
  9. 49 remaining items

  10. hannah23280 commented on Jan 2, 2023

    @hannah23280

    Since today , it is very common to have UI (angular) and business logic to be situated in different machines (hence different origin), thus angular should be updated to allow include X-XSRF automatically , and not worry about same or different origin. Or at least provide an option for developer to enable this.

  11. Loque18 commented on Feb 11, 2023

    @Loque18

    +1 to this issue,
    I spent +24 hours trying to figure out what was wrong, it almost made me cry, I thought it was the server, so I started from scratch like 10 times, until I decided to try with axios, and I found out that the error was that the X-XSRF-TOKEN was not automatically added to the headers, I added it manually and it worked, then I found this post on stack overflow that shows a workaround, and then I finally discover this issue.

    It worked with the relative path //localhost:8000, as everyone mentioned it is really confusing, can someone explain why absolute paths don't work ? I didn't even know it was possible to use relative paths, and it feels weird, we are in the 15.x version of angular, this definitely needs to be fixed or to be documented, to avoid other devs to waste time

    this is my code just in case anyone need it

    const options = {
        withCredentials: true,
    };
    
    this.http.get('http://localhost:8000/sanctum/csrf-cookie').subscribe(() => {
        console.log('csrf-cookie');
    
        this.http.post('//localhost:8000/login', data, options).subscribe(res => {
            console.log('login', res);
        });
    });
  12. michaelurban commented on Feb 11, 2023

    @michaelurban

    @Loque18 Your issue: "Documenting things to keep devs from wasting their time" was reported and immediately closed as "not a priority" during the Angular 2 RC process.

    This is what I found as of a few years ago: #20511 (comment)

    Any Angular codebase is going to be filled with notes about undocumented behaviors and workarounds for obvious defects that reference years old tickets.

    it almost made m[e] cry

    Welcome to the club, we had jackets made.. So many tear stained jackets.

  13. RNX339 commented on May 26, 2023

    @RNX339

    Solution :
    csrf-interceptor.ts
    import { Injectable } from '@angular/core';
    import {
    HttpInterceptor,
    HttpHandler,
    HttpRequest,
    HttpEvent,
    HttpXsrfTokenExtractor,
    } from '@angular/common/http';
    import { Observable } from 'rxjs';

    @Injectable()
    export class CsrfInterceptor implements HttpInterceptor {
    constructor(private csrfExtract: HttpXsrfTokenExtractor) {}
    intercept(
    req: HttpRequest,
    next: HttpHandler
    ): Observable<HttpEvent> {
    // Get csrf from the cookie.
    const csrf = this.csrfExtract.getToken()?.toString() || '';
    // Clone the request and replace the original headers with
    // cloned headers, updated with csrf.
    const csrfReq = req.clone({
    headers: req.headers.set('X-XSRF-TOKEN', csrf),
    withCredentials: true,
    });

    // send cloned request with header to the next handler.
    return next.handle(csrfReq);
    

    }
    }
    app.modules.ts
    ...
    providers: [
    [{ provide: HTTP_INTERCEPTORS, useClass: CsrfInterceptor, multi: true }],
    ],

  14. lodistack commented on Oct 3, 2024

    @lodistack

    +1 to this issue, I spent +24 hours trying to figure out what was wrong, it almost made me cry, I thought it was the server, so I started from scratch like 10 times, until I decided to try with axios, and I found out that the error was that the X-XSRF-TOKEN was not automatically added to the headers, I added it manually and it worked, then I found this post on stack overflow that shows a workaround, and then I finally discover this issue.

    It worked with the relative path //localhost:8000, as everyone mentioned it is really confusing, can someone explain why absolute paths don't work ? I didn't even know it was possible to use relative paths, and it feels weird, we are in the 15.x version of angular, this definitely needs to be fixed or to be documented, to avoid other devs to waste time

    this is my code just in case anyone need it

    const options = {
    withCredentials: true,
    };

    this.http.get('http://localhost:8000/sanctum/csrf-cookie').subscribe(() => {
    console.log('csrf-cookie');

    this.http.post('//localhost:8000/login', data, options).subscribe(res => {
        console.log('login', res);
    });
    

    });

    I faced the same issue while integrating the Laravel Breeze (Santumn) API backend. And I am not sure yet how to work around this.
    Here is my interceptor:

    import {
        HttpErrorResponse,
        HttpEvent,
        HttpHandlerFn,
        HttpRequest,
    } from '@angular/common/http';
    import { inject } from '@angular/core';
    import { AuthService } from 'app/core/auth/auth.service';
    import { Observable, catchError, throwError } from 'rxjs';
    
    /**
     * Intercept
     *
     * @param req
     * @param next
     */
    export const authInterceptor = (
        req: HttpRequest<unknown>,
        next: HttpHandlerFn
    ): Observable<HttpEvent<unknown>> => {
        const authService = inject(AuthService);
    
        console.log('req :>> ', req);
        const authReq = req.clone({
            withCredentials: true, // Ensures cookies are sent with the request
            headers: req.headers.set('X-Requested-With', 'XMLHttpRequest'),
        });
        console.log('authReq :>> ', authReq);
    
        return next(authReq).pipe(
            catchError((error: any) => {
                // If we receive a 401 Unauthorized response, logout the user
                if (error instanceof HttpErrorResponse && error.status === 401) {
                    authService.signOut();
                    // location.reload();
                }
                return throwError(() => error);
            })
        );
    };
  15. JeanMeche commented on Oct 1, 2025

    @JeanMeche
    Member

    The documentation does mention:

    By default, an interceptor sends this header on all mutating requests (such as POST) to relative URLs, but not on GET/HEAD requests or on requests with an absolute URL.

    https://angular.dev/best-practices/security#httpclient-xsrf-csrf-security

    Today this is a hard coded configuration.

  16. added 2 commits that reference this issue on Nov 27, 2025
    9db3f80
    1c3b026
  17. angular-automatic-lock-bot commented on Jan 3, 2026

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  18. locked and limited conversation to collaborators on Jan 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgentarea: common/httpIssues related to HTTP and HTTP Clientfreq2: mediumtype: bug/fixtype: confusing

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions