High Traffic to /libs/granite/csrf/token.json and /libs/granite/security/currentuser.json in AEM – How to Optimize? | Community
Skip to main content
Level 4
March 27, 2026
Question

High Traffic to /libs/granite/csrf/token.json and /libs/granite/security/currentuser.json in AEM – How to Optimize?

  • March 27, 2026
  • 7 replies
  • 131 views

Hi Team,

We are observing a very high volume of requests hitting AEM internal Granite APIs:

  • /libs/granite/csrf/token.json (~618K requests, ~19%)
  • /libs/granite/security/currentuser.json (~300K requests, ~9.3%)

Together, these endpoints account for nearly 29% of total traffic in our logs.

We understand these endpoints are used for authentication/session handling, but this volume seems unusually high and is impacting overall traffic patterns.

We would like guidance on:

  1. Is this level of traffic expected for these endpoints in Adobe Experience Manager (especially Cloud Service)?
  2. What are the recommended best practices to reduce or optimize calls to these APIs?
         
  • Dispatcher/CDN rules?
  • Frontend implementation improvements?

 

  1. Could this indicate a misconfiguration (e.g., frequent polling, missing caching, or bot traffic)?
  2. Are there Adobe-recommended patterns to minimize repeated CSRF token and current user calls?
  3. Any insights or similar experiences would be really helpful.

    Thanks in advance!

    7 replies

    Level 2
    March 28, 2026

    Hi ​@georhe6,

    This level of traffic does not reflect an optimal implementation. 

     

    Calls to “/libs/granite/csrf/token.json” should ideally be limited to "One per user session", as the CSRF token is session based and does not need to be fetched repeatedly.

    Similarly, “/libs/granite/security/currentuser.json” should only be invoked when user information is actually required, rather than on every request or component load. 

     

    And yes, frequent polling introduces unnecessary load on the system and is not recommended. Instead, go with caching or event-driven approaches. It is recommended to implement client side caching, preferably using session storage so that values persist across the user session without repeated API calls. This helps to reduce unnecessary traffic. 

     

    If the current implementation involves multiple components independently making authentication related calls, it would be more efficient to refactor this into a centralized approach, where a single service manages CSRF tokens and user data. 

     

    Additionally, if required implement rate limiting per IP at the CDN layer.

     

    Note: Both “/libs/granite/security/currentuser.json” and “/libs/granite/csrf/token.json” must not be cached at the CDN or Dispatcher level, because they contain per-user/session data which are dynamic.

    Jineet_Vora
    Community Advisor and Adobe Champion
    Community Advisor and Adobe Champion
    March 30, 2026

    @georhe6 - This needs further analysis onto why the requests are more. Particularly, are there any user(s) from which these requests are originating? Any integration user account is responsible in making these many calls?

    georhe6Author
    Level 4
    March 31, 2026

    Thanks ​@ruchith53 ​@Jineet_Vora ,
    Its a public website , no authentication is required. on each page load and each component load we are observing the token is getting called ,and its getting initated by etc.client.lib/clientlibs/granite/jquery/granite/csrf.lc*js

    georhe6Author
    Level 4
    April 1, 2026

    We observed a high number of requests to /csrf/token.json, especially on the publish instance. To optimize this, we made the following changes:

     

    Changes Implemented:

    1. Removed unnecessary client library dependency

    We removed the following category from clientlib-base/.content.xml:

    core.wcm.components.form.container.v2
    2. Restricted ContextHub execution to Author only

    Previously, ContextHub was being executed on both Author and Publish instances. We updated the implementation to load it conditionally only in Author (Edit/Preview modes):

    <sly data-sly-test="${wcmmode.edit || wcmmode.preview}"
    data-sly-resource="${'contexthub' @ resourceType='granite/contexthub/components/contexthub'}"

    Outcome:

    • Publish Instance:
      • /csrf/token.json calls are no longer triggered.
    • Author Instance:

                     Significant reduction in the number of CSRF token requests.
     

    Currently, none of our custom features has CSRF protection

    Form submissions (POST/PUT/DELETE)

    AJAX calls that modify data

    We plan to explicitly implement CSRF token handling only when such features are introduced. That is the currect approach right 

    However the API calls are not getting failed , its passing and in  publish aemrequest log i got one CSRF token entry

    @Jineet_Vora  ​@ruchith53

    AmitVishwakarma
    Community Advisor
    Community Advisor
    April 1, 2026

    Hi ​@georhe6 

    For a public, anonymous site your observations and changes are directionally correct, with a few important guard‑rails.

    1. Is that much traffic "normal"?

    2. Are your changes "the right approach"?

    The changes you listed are a good move for a public, read‑only site:

    • Removing the form container clientlib dependency (which was pulling in the CSRF JS) so it doesn't run by default on every page is fine.
    • Restricting ContextHub to Author (edit/preview) only is also best practice; ContextHub and personalization logic shouldn't run on anonymous publish unless actually needed.
    • Result: fewer /csrf/token.json calls on publish and reduced noise on author – this is expected and positive.

    This is safe as long as:

    • You truly have no state‑changing POST/PUT/DELETE to AEM (forms, workflows, XHR updates, etc.) on publish right now.
    • You keep the CSRF filter & endpoints enabled server‑side; you've only removed the automatic client fetch, not the protection mechanism itself.

    When you later add forms or write operations, you must:

    3. What would be the long‑term best practice?

    For when you do introduce POST/AJAX features:

    • Don't fetch /csrf/token.json on every component load.
    • Instead:
      • Fetch token once per page (or per user session).
      • Cache it on the client (e.g. in memory or sessionStorage) and reuse it.
      • Optionally centralize this in a small "auth/session service" JS module so all components call that instead of the Granite endpoints directly.
    • Keep /libs/granite/security/currentuser.json calls limited to places that truly need user info, not every render.

    So yes: for a public, read‑only site your current change set is sensible. Just document clearly that when forms or other write operations are introduced, CSRF token fetching must be re‑introduced in a controlled, centralized way.

    Amit Vishwakarma - Adobe Commerce Champion 2025 | 16x Adobe certified | 4x Adobe SME
    partyush
    Community Advisor
    Community Advisor
    April 3, 2026

    Hi ​@georhe6 

    Hey! You are 100% on the right track here, I can tell you that the fixes you just implemented are exactly what I would have prescribed.

    Here is the straightforward breakdown of why your approach is correct, why you saw those logs, and how to handle this in the future.

    1. Your Fixes are Spot On Removing the global form container clientlib dependency and locking ContextHub strictly to the Author tier are textbook solutions.

    • ContextHub is notorious for spamming currentuser.json to figure out who is browsing. On a public, anonymous site, it’s pure dead weight.

    • The Granite CSRF clientlib is aggressively greedy; if it loads on a page, it fetches a token immediately, regardless of whether a form actually exists on that page.

    2. Is this the right approach since you have no POST/PUTs? Absolutely. If your publish site is read-only (only GET requests), you do not need CSRF protection on the frontend. AEM's server-side CSRF filter automatically ignores GET, HEAD, and OPTIONS requests anyway. Ripping this out now saves you massive compute overhead on your Publish instances and improves your cache hit ratio.

    3. Why were the calls "passing" in the logs? You noticed the calls were returning 200 OK instead of failing. This is expected. If a script asks AEM for a CSRF token, AEM will happily generate and return one, even for an anonymous user. It wasn't failing; it was just a complete waste of server resources because that token was never going to be used.

    How to handle this in the future (The Best Practice) When you eventually do introduce forms, login modals, or custom AJAX POST calls, do not add that global clientlib back. Instead, implement a "Just-In-Time" (JIT) fetch:

    1. Do not load the token on page load.

    2. Write a lightweight JS utility that calls token.json only when a user interacts with a form (e.g., on the first focus event of an input field, or right before the submit action).

    3. Store that fetched token in the browser's sessionStorage so subsequent form actions on the site can just reuse it without hitting AEM again.

    You did the right thing. Keep the publish traffic clean!

    Thanks 
    Partyush

    PGURUKRISHNA
    Level 5
    April 6, 2026

    Hey ​@georhe6 , Confirm what is triggering these calls..

    Step 1: Block These Endpoints on Publish Dispatcher

    Add deny rules in your Dispatcher 

    filters.any

     so anonymous publish traffic never reaches AEM:

    # deny access to CSRF token on publish
    /0200 { /type "deny" /url "/libs/granite/csrf/token.json" }

    # deny access to currentuser on publish
    /0201 { /type "deny" /url "/libs/granite/security/currentuser.json" }

     

    Step 2: Cache CSRF Token on the Client Side

    Replace all repeated fetch calls with a single cached fetch:

    let _csrfToken = null;

    async function getCsrfToken() {
    if (_csrfToken) return _csrfToken;
    const resp = await fetch('/libs/granite/csrf/token.json', { credentials: 'same-origin' });
    const data = await resp.json();
    _csrfToken = data.token;
    return _csrfToken;
    }

     

    Step 3: Cache Current User on the Client Side

    async function getCurrentUser() {
    const cached = sessionStorage.getItem('aem-current-user');
    if (cached) return JSON.parse(cached);
    const resp = await fetch('/libs/granite/security/currentuser.json', { credentials: 'same-origin' });
    const data = await resp.json();
    sessionStorage.setItem('aem-current-user', JSON.stringify(data));
    return data;
    }

     

    Step 4: Add CDN Traffic Filter Rules (AEMaaCS)

    Create or update 

    dispatcher/src/conf.d/cdn.yaml

    :

    kind: CDN
    version: '1'
    data:
    trafficFilters:
    rules:
    - name: rate-limit-csrf
    when:
    allOf:
    - { reqProperty: path, equals: /libs/granite/csrf/token.json }
    - { reqProperty: tier, equals: publish }
    action:
    type: block
    - name: rate-limit-currentuser
    when:
    allOf:
    - { reqProperty: path, equals: /libs/granite/security/currentuser.json }
    - { reqProperty: tier, equals: publish }
    action:
    type: block

     

     

    Step 5: Remove Unnecessary Frontend Polling

    Search your JS codebase for any 

    setInterval

     or repeated calls:

    grep -rn "csrf/token.json\|currentuser.json\|setInterval" ui.frontend/src/ ui.apps/

     

    Remove or refactor any:

    •  

      setInterval
       polling these endpoints
    • Calls on every page load or route change

    • Calls before every AJAX request without reusing the cached value

    Step 6: Block Bot Traffic

    Add bot filtering in your Apache vhost config:

    <LocationMatch "^/libs/granite/(csrf|security)/">
    SetEnvIfNoCase User-Agent "bot|crawl|spider|slurp|Googlebot|Bingbot" is_bot
    Order Allow,Deny
    Allow from all
    Deny from env=is_bot
    </LocationMatch>

     

    Step 7: Validate the Fix

    After deploying, monitor your access logs:

    # Check if traffic to these endpoints has dropped
    grep -c "csrf/token.json" access.log
    grep -c "currentuser.json" access.log

     

     


     

    Pagidala GuruKrishna