1

Reeeallly hoping someone can help me out here, as this has been plaguing me for a while and no amount of googling has helped. I am a website developer hobbyest who's trying to create a contest entry app for a non-profit org.

When I load a page in my web application, I see 2 sessions created in my custom web app folder (I am only expecting one):

list of sessions

This is, of course, problematic as I'm setting session values to be used by downstream PHP code and depending on which one is grabbed my code fails on checking for my CSRF token.

Here's what's happening (Chrome browser):

  1. Force-reload page to update cache, as I'm working on JS/CSS changes; I do not submit the form or navigate anywhere.
  2. My log files show two different session_ids: session_id:ccbbhescjqevk02id43nf6tmgc and session_id:vf7fgnu92up1uim9nlup1h8nlu (the latter is only created at the very end of the page load)
  3. I'm adding a CSRF token to my form which gets submitted via POST and I check that in my downstream PHP file, but sometimes this fails as it's not in the session it's using.

Here's my page:

<?php 

    // include setConfig
    require_once '../../../private/contest/include/setConfig.php'; 

    // include startSession
    require_once '../../../private/contest/include/startSession.php';

    if (session_status() == PHP_SESSION_NONE) {
        startWFsession('entry_form');
    }

    $_SESSION['current_time'] = time();    

    // Blank out assoc array of new entry details that are used by printout.php
    unset($_SESSION['newEntryRec']);

    $pageName = 'entryForm';
    $pageVer = '1.0.0';

    $ip =  getUserIP();
    $sessionID = session_id();

    // Generate form token for CSRF protection
    if (!isset($_SESSION['token'])) {
        $_SESSION['token'] = generateCSRFtoken($sessionID, $ip);
    }
    $token = $_SESSION['token'];

    if (DEV_MODE) {
        $log->debug('CSRF token for this session:', 
                array('file:' => basename(__FILE__), 
                        'line#:' => __LINE__,
                        'session_id' => $sessionID,
                        'IP' => $ip,
                        '$_SESSION["token"]' => bin2hex($_SESSION['token'])));
    }

?>
<!DOCTYPE html>
<html lang="en-US">
    <head>
        <meta name="robots" content="noindex,nofollow" />   
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1">
.
.
. 
        <title>Testing 123</title>

        <noscript>
            <!-- Hide page contents if JS is disabled -->
            <style>
                .pagecontainer {display:none;}
            </style>
        </noscript>

        <script src="js/entrylogic_v1.0.0.js"></script>
        <script src="https://www.google.com/recaptcha/api.js" async defer></script>

    </head>
    <body> 

        <div>
            <?php include('../common/html/navbar.htm');?>
        </div>

        <noscript>   
            <div class="noscriptmsg">
                <h4><span class="red">JavaScript</span> is disabled on your browser. You will not be able to use this application until it is enabled.</h4>
                <h5>Please refer to your browser's specific configuration details to change this setting.</h5>
            </div>
        </noscript>

        <div class="pagecontainer">

            <div id="outline">         

                <div class="container">

                    <?php
                        // Determine if this year's contest is open
                        // ========================================
                        $current_time = new DateTime('now');
                        $chkContest = isContestOpen($current_time, $sessionID, $ip);

                        $log->debug('Function isContestOpen check ',
                                array('file:' => basename(__FILE__),
                                    'line#:' => __LINE__,
                                    'session_id' => $sessionID,
                                    'IP' => $ip,
                                    'chkContest' => $chkContest));
                        if ($chkContest != 'open' || is_null($chkContest)) {
                            exit($chkContest);
                        }

                        // Got this far, so contest is open
                    ?>

                    <div class="form-row">
                        <!--<div class="center">-->

                            <form id="entryForm" name="entryForm" class="form-horizontal" enctype="multipart/form-data">                 

                                <!-- CSRF protection -->
                                <input type="hidden" name="token" value="<?php echo $token; ?>" />

                                <div class="entryFields">

                                    <div>        
                                        <fieldset class="scheduler-border">
                                            <legend class="scheduler-border">Entrant Info</legend>                                  

                                            <div class="form-group">
                                                <div class="col-sm-4">
                                                    <label for="first-name" class="sr-only">First name</label>
                                                    <input id="first-name" name="fName" class="form-control" type="text" placeholder="First name [required]" title="Requried. Max <?php echo FORM_NAME_FLD_LENGTH; ?> characters" 
                                                           maxlength="<?php echo FORM_NAME_FLD_LENGTH; ?>" data-wf-fld-len="<?php echo FORM_NAME_FLD_LENGTH; ?>" required autofocus autocomplete="given-name">
                                                </div>
                                                <div class="col-sm-8">
                                                    <label for="last-name" class="sr-only">Last name</label>
                                                    <input id="last-name" name="lName" class="form-control" type="text" placeholder="Last name [required]" title="Requried. Max <?php echo FORM_NAME_FLD_LENGTH; ?> characters" 
                                                           maxlength="<?php echo FORM_NAME_FLD_LENGTH; ?>" data-wf-fld-len="<?php echo FORM_NAME_FLD_LENGTH; ?>" required autocomplete="family-name">
                                                </div>
                                            </div>

                                            <div class="form-group">
                                                <div class="col-sm-12">
                                                    <label for="email" class="sr-only">Email</label>
                                                    <input id="email" name="email" class="form-control" type="email" placeholder="Email [required]"
                                                           title="Required. Max <?php echo FORM_EMAIL_FLD_LENGTH; ?> characters (e.g. bela@transylvania.com)" maxlength="<?php echo FORM_EMAIL_FLD_LENGTH; ?>"
                                                           data-wf-fld-len="<?php echo FORM_EMAIL_FLD_LENGTH; ?>" required autocomplete="home email">
                                                </div>
                                            </div>                                    

                                        </fieldset>                                    
                                    </div>        

                                    <div>        
                                        <fieldset class="scheduler-border">
                                            <legend class="scheduler-border">Model Info</legend>

                                            <div class="form-group">
                                                <div class="col-sm-12">
                                                    <label for="model_name" class="sr-only">Model name</label>
                                                    <textarea rows="2" id="model_name" name="modelName" class="form-control textarea-group-lg" placeholder="Title or name of entry [required]" 
                                                              title="Required. Max <?php echo FORM_MODELNAME_FLD_LENGTH; ?> characters" maxlength="<?php echo FORM_MODELNAME_FLD_LENGTH; ?>" 
                                                              data-wf-fld-len="<?php echo FORM_MODELNAME_FLD_LENGTH; ?>" required></textarea>
                                                </div>
                                            </div>                                   

                                            <div class="center">  

                                                <div class="form-group"><!--category -->
                                                    <div  class="col-sm-12">
                                                        <label for="category" class="sr-only">Category</label>
                                                        <!-- https://developer.snapappointments.com/bootstrap-select/options/#default-settings -->
                                                        <select id="category" name="category" class="selectpicker show-tick" data-show-subtext="true" data-size="10" data-live-search="true" data-width="auto" 
                                                                title="Category [required]" data-header="Select the most appropriate category:" size="1" required>

                                                            <?php



                                                                $log->debug('About to call getCategories() ...',
                                                                        array('file:' => basename(__FILE__),
                                                                            'line#:' => __LINE__,
                                                                            'session_id' => $sessionID,
                                                                            'IP' => $ip));                                                            



                                                                // Call function to get categories for current year
                                                                $result = getCategories(date('Y'), $sessionID, $ip);

                                                                if (!empty($result)){?>
                                                                    <option disabled selected value>Category [required]</option>

                                                                    <?php
                                                                    for ($i = 0; $i < count($result); $i++){
                                                                        //echo nl2br ($result[$i]["Category"] . "\n");?>
                                                                        <option value="<?php echo $result[$i]['ID']; ?>" data-subtext="<?php echo htmlspecialchars($result[$i]['Description']); ?>"><?php echo htmlspecialchars($result[$i]['Category']); ?></option><?php
                                                                    }
                                                                } else {?>
                                                                    <option disabled selected value>No categories exist for current year</option><?php
                                                                }
                                                            ?>
                                                        </select>
                                                    </div>
                                                </div><!--end of category -->

                                                <?php
                                                    $log->debug('After call getCategories() ...',
                                                            array('file:' => basename(__FILE__),
                                                                'line#:' => __LINE__,
                                                                'session_id' => $sessionID,
                                                                'IP' => $ip));                                                 
                                                ?>

                                            </div>

                                            <div class="form-group">
                                                <div  class="col-sm-12">
                                                    <label for="exampleFormControlFile1">Select up to four (4) model photos (PNG or JPG only):</label>
                                                    <input type="file" class="form-control-file" id="modelPhoto1" name="modelPhoto1" accept=".jpg, .png, .jpeg|image/*" required>
                                                    <input type="file" class="form-control-file" id="modelPhoto2" name="modelPhoto2" accept=".jpg, .png, .jpeg|image/*">
                                                    <input type="file" class="form-control-file" id="modelPhoto3" name="modelPhoto3" accept=".jpg, .png, .jpeg|image/*">
                                                    <input type="file" class="form-control-file" id="modelPhoto4" name="modelPhoto4" accept=".jpg, .png, .jpeg|image/*">

                                                </div>
                                            </div>

                                        </fieldset>   

                                    </div>                        

                                    <div>        
                                        <fieldset class="scheduler-border">
                                            <legend class="scheduler-border">Terms and Conditions</legend>   
                                            <p style="text-align:left;font-size:x-small;margin:0px 0px 0px 0px">I grant WANT-A-FEST permission to use my model photos in their marketing materials without limitation or restriction.</p>
                                            <p style="margin-top:4px;margin-bottom:4px;font-size:small"><input id="acceptAgrmt" type="checkbox" name="acceptAgrmt" value="" required> I accept the Terms and Conditions</p>
                                        </fieldset>
                                    </div>                               

                                    <div id='recaptcha' class="g-recaptcha" data-sitekey="<?php echo RECAPTCHA_SITE_KEY; ?>" data-callback="onSubmit" data-size="invisible"></div>

                                    <div class="center">
                                        <div><!--form buttons-->
                                            <input type="submit" id="formsubmit" name="mysubmit" class="btn btn-danger" value="Submit">
                                            <input type="reset" id="formreset" class="btn btn-warning" value="Clear Form">
                                        </div><!--end of form buttons-->

                                        <div class="center" style="margin-top: 10px;font-size: x-small">
                                            This site is protected by reCAPTCHA and the Google 
                                                <a href="https://policies.google.com/privacy">Privacy Policy</a> and
                                                <a href="https://policies.google.com/terms">Terms of Service</a> apply.
                                        </div>
                                    </div>

                                </div>

                            </form>  

                        <!--</div>-->
                    </div>
                </div><!--container-->  
            </div><!--outline-->        
        </div><!--pagecontainer-->

        <div class="modalLoading">
            <?php
                $log->debug('Before loading modalLoading.htm ...',
                        array('file:' => basename(__FILE__),
                            'line#:' => __LINE__,
                            'session_id' => $sessionID,
                            'IP' => $ip));                                                 
            ?>

            <?php include('../common/html/modalLoading.htm');?>

            <?php
                $log->debug('After loading modalLoading.htm ...',
                        array('file:' => basename(__FILE__),
                            'line#:' => __LINE__,
                            'session_id' => $sessionID,
                            'IP' => $ip));                                                 
            ?>
        </div>

        <!-- SHOPPING CART START -->
        <?php
            $log->debug('Before loading shopping cart stuff ...',
                    array('file:' => basename(__FILE__),
                        'line#:' => __LINE__,
                        'session_id' => $sessionID,
                        'IP' => $ip));                                                 
        ?>

        <?php include('../common/html/jquery_dlg_clear_cart.htm');?>
        <?php include('../common/html/jquery_dlg_delete_entry.htm');?>

        <?php
            $log->debug('After loading shopping cart stuff ...',
                    array('file:' => basename(__FILE__),
                        'line#:' => __LINE__,
                        'session_id' => $sessionID,
                        'IP' => $ip));                                                 
        ?>
        <!-- SHOPPING CART END -->

        <div class="footer">
            <?php
                $log->debug('Before loading footer.htm ...',
                        array('file:' => basename(__FILE__),
                            'line#:' => __LINE__,
                            'session_id' => $sessionID,
                            'IP' => $ip));                                                 
            ?>
            <?php include('../common/html/footer.htm');?>
            <?php
                $log->debug('After loading footer.htm ...',
                        array('file:' => basename(__FILE__),
                            'line#:' => __LINE__,
                            'session_id' => $sessionID,
                            'IP' => $ip));                                                 
            ?>

        </div>

    </body>
</html>

My custom session start function:

function startWFsession($sessionName) {
    global $log, $entryFormCookieParams;

    // From http://php.net/manual/en/function.session-set-cookie-params.php :
    session_set_cookie_params(
        $entryFormCookieParams["lifetime"],
        $entryFormCookieParams["path"],
        $entryFormCookieParams["domain"],
        $entryFormCookieParams["secure"],
        $entryFormCookieParams["httponly"]
    );

    // Set custom PHP session save path before every call to session_start() as per Dean Brook's (Iglou) suggestion
    ini_set('session.save_path', PHP_SESSION_FLDR);

    // Set session timeout to be the same as the cookie
    ini_set('session.gc-maxlifetime', $entryFormCookieParams["lifetime"]);

    // Set session srict mode on (rejects session IDs not initialized by the server)
    ini_set('session.use_strict_mode', true);

    if (!empty($sessionName)) {
        session_name($sessionName);            
    }

    session_start();

    setcookie(session_name(),session_id(),time()+$entryFormCookieParams["lifetime"]);   

    $log->debug('Session started or resumed - check session_id', 
            array('file:' => basename(__FILE__), 
                    'line#:' => __LINE__,
                    'session_id' => session_id(),
                    'session_name' => session_name(),
                    'Cookie lifetime' => $entryFormCookieParams["lifetime"],
                    'Cookie path' => $entryFormCookieParams["path"],
                    'Cookie domain' => $entryFormCookieParams["domain"],
                    'Cookie secure' => $entryFormCookieParams["secure"],
                    'Cookie httponly' => $entryFormCookieParams["httponly"],
                    'IP' => getUserIP()
    ));

    return is_session_started() ? TRUE : FALSE;
    }

My log file output:

[2020-06-09 19:13:12] WF.DEBUG: Reading config settings and setting file includes {"file:":"setConfig.php","line#:":48,"$_SERVER[\"DOCUMENT_ROOT\"]":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/public_html","DB_SERVERNAME":"localhost","DB_NAME":"wantafest_contest_online","DB_SERVER_PORT":3311,"DB_USERNAME":"root","EMAIL_HOST":"mail.wonderfest.com","EMAIL_SMTP_AUTH":true,"EMAIL_USERNAME":"x.com","EMAIL_SMTP_SECURE":"tls","EMAIL_SERVER_PORT":587,"EMAIL_SEND_LOGS_TO":"y.com","EMAIL_SEND":false,"LOG_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/private/contest/logs/","CACHE_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/private/contest/cached-files","CACHE_EXPIRE_SQL_SEC":59,"CACHE_EXPIRE_PRINTOUT_SEC":360,"PHOTOS_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/private/contest/photos/","BO_EXPIRE_SESSION_AFTER_SEC":900,"$entryFormCookieParams[\"lifetime\"]":1800,"$entryFormCookieParams[\"path\"]":"/","$entryFormCookieParams[\"domain\"]":".localhost","$entryFormCookieParams[\"secure\"]":false,"$entryFormCookieParams[\"httponly\"]":true,"PHP_SESSION_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/sessions"} 
[2020-06-09 19:13:12] WF.DEBUG: Session started or resumed - check session_id {"file:":"startSession.php","line#:":41,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","session_name":"entry_form","Cookie lifetime":1800,"Cookie path":"/","Cookie domain":".localhost","Cookie secure":false,"Cookie httponly":true,"IP":"::1"} 
[2020-06-09 19:13:12] WF.DEBUG: PHP version is 5.4.0 or greater {"file:":"startSession.php","line#:":65,"IP":"::1","phpversion()":"7.4.2","session_status()":2} 
[2020-06-09 19:13:12] WF.DEBUG: generateCSRFtoken {"file:":"commonfunctions.php","line#:":910,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1","token":"64373566386535363733353835303938623364643931333838643636643030373736346332396136303432626634306266303366383433386361633530376465"} 
[2020-06-09 19:13:12] WF.DEBUG: CSRF token for this session: {"file:":"entryForm_v1.0.0.php","line#:":36,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1","$_SESSION[\"token\"]":"64373566386535363733353835303938623364643931333838643636643030373736346332396136303432626634306266303366383433386361633530376465"} 
[2020-06-09 19:13:12] WF.DEBUG: Bypassing date check - DEV MODE {"file:":"commonfunctions.php","line#:":176,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"} 
[2020-06-09 19:13:12] WF.DEBUG: Function isContestOpen check  {"file:":"entryForm_v1.0.0.php","line#:":253,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1","chkContest":"open"} 
[2020-06-09 19:13:12] WF.DEBUG: About to call getCategories() ... {"file:":"entryForm_v1.0.0.php","line#:":331,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"} 
[2020-06-09 19:13:12] WF.DEBUG: Cache key "categories" found. {"file:":"commonfunctions.php","line#:":393,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1","Year":"2020"} 
[2020-06-09 19:13:12] WF.DEBUG: After call getCategories() ... {"file:":"entryForm_v1.0.0.php","line#:":359,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"} 
[2020-06-09 19:13:12] WF.DEBUG: Before loading modalLoading.htm ... {"file:":"entryForm_v1.0.0.php","line#:":418,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"} 
[2020-06-09 19:13:12] WF.DEBUG: After loading modalLoading.htm ... {"file:":"entryForm_v1.0.0.php","line#:":428,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"} 
[2020-06-09 19:13:12] WF.DEBUG: Before loading shopping cart stuff ... {"file:":"entryForm_v1.0.0.php","line#:":438,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"} 
[2020-06-09 19:13:12] WF.DEBUG: After loading shopping cart stuff ... {"file:":"entryForm_v1.0.0.php","line#:":449,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"} 
[2020-06-09 19:13:12] WF.DEBUG: Before loading footer.htm ... {"file:":"entryForm_v1.0.0.php","line#:":459,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"} 
[2020-06-09 19:13:12] WF.DEBUG: After loading footer.htm ... {"file:":"entryForm_v1.0.0.php","line#:":467,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"} 
[2020-06-09 19:13:12] WF.DEBUG: Reading config settings and setting file includes {"file:":"setConfig.php","line#:":48,"$_SERVER[\"DOCUMENT_ROOT\"]":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/public_html","DB_SERVERNAME":"localhost","DB_NAME":"wantafest_contest_online","DB_SERVER_PORT":3311,"DB_USERNAME":"root","EMAIL_HOST":"mail.wonderfest.com","EMAIL_SMTP_AUTH":true,"EMAIL_USERNAME":"x.com","EMAIL_SMTP_SECURE":"tls","EMAIL_SERVER_PORT":587,"EMAIL_SEND_LOGS_TO":"y.com","EMAIL_SEND":false,"LOG_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/private/contest/logs/","CACHE_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/private/contest/cached-files","CACHE_EXPIRE_SQL_SEC":59,"CACHE_EXPIRE_PRINTOUT_SEC":360,"PHOTOS_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/private/contest/photos/","BO_EXPIRE_SESSION_AFTER_SEC":900,"$entryFormCookieParams[\"lifetime\"]":1800,"$entryFormCookieParams[\"path\"]":"/","$entryFormCookieParams[\"domain\"]":".localhost","$entryFormCookieParams[\"secure\"]":false,"$entryFormCookieParams[\"httponly\"]":true,"PHP_SESSION_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/sessions"} 
[2020-06-09 19:13:12] WF.DEBUG: Session started or resumed - check session_id {"file:":"startSession.php","line#:":41,"session_id":"**vf7fgnu92up1uim9nlup1h8nlu**","session_name":"entry_form","Cookie lifetime":1800,"Cookie path":"/","Cookie domain":".localhost","Cookie secure":false,"Cookie httponly":true,"IP":"::1"} 
[2020-06-09 19:13:12] WF.DEBUG: PHP version is 5.4.0 or greater {"file:":"startSession.php","line#:":65,"IP":"::1","phpversion()":"7.4.2","session_status()":2} 

I'm using MAMP Pro 5.7 on macOS 10.14.6. My PHP version is 7.4.2

EDIT Per @verjas' suggestion, I moved my favicon to a specific location and updated my page to use that. I can see that icon in the browser tab, and when I view-source I see this:

<!DOCTYPE html>
<html lang="en-US">
    <head>
        <meta name="robots" content="noindex,nofollow" />   
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
        <link rel="shortcut icon" href="/contest/common/img/Favicon.png"/>
.
.
.

But looking in my Apache error log, I see this:

[Wed Jun 10 13:53:34 2020] [error] [client ::1] File does not exist: /Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/public_html/favicon.ico, referer: http://want-a-fest:8888/contest/entry/entryForm.php

What the? My page shouldn't be looking for it in the public_html root, and the file name is different (should be "Favicon.png"). Do I have to have a "favicon.ico" in the public_html even if it is not being referenced?

RossW
  • 215
  • 1
  • 7
  • 16
  • I haven't used session in a log time, but I think `session_start()` sends the cookie automatically, if I remember correctly? Do you have a reason to change the default session? Although totally valid, I think the defaults work by and large out of the box for average scenarios, but like I said, I don't use sessions too much anymore. – Chris Haas Jun 10 '20 at 00:05
  • Thanks Chris. I wanted to set specific timeouts and also a custom session file storage location as they were getting purged on the public site accidentally. – RossW Jun 10 '20 at 00:53
  • 1
    Hi. This reminds me of a problem that bugged my for a few hours with sessions and CSRFs: the browser was requesting the page twice (visible only on logs), so that made the CSRF invalid on the Post submit. This was because of: favicon request (https://stackoverflow.com/questions/3926943/site-is-called-twice). I'm not sure this applies here, but for me, either creating a favicon or rerouting calls for it solved it. – verjas Jun 10 '20 at 07:12
  • @verjas: I have a favicon.ico file in my public_html directory along with my index.html file, but this page is down a directory or two. Are you suggesting added this to my HEADER section explicitly? – RossW Jun 10 '20 at 12:47
  • 1
    @verjas: THANK YOU!!! I did have a favicon.ico in my public_html root but the page I was force reloading was down a directory or two. So, if I navigated to my page via the index.html in the root, all was fine; if, however, I forced a reload then that's when I saw 2 sessions. I copied my favicon.ico to a common image folder and linked that in the HEAD section of my page, and viola - no more double sessions! – RossW Jun 10 '20 at 13:05
  • :) I'm glad it worked. It was a tricky one for me too. – verjas Jun 10 '20 at 13:14
  • @verjas: spoke too soon. after a few server restarts it started happening again. Sigh ... – RossW Jun 10 '20 at 15:18
  • Sorry man. I don't have other ideas here. When I had that problem, I checked in the raw access log in the server. I could see clearly that when I used a hard-reload (or incognito), there was an extra request from the browser to check for favico. After I solved that, no more problems. Do you have a log like that? Can you check what requests are made there? – verjas Jun 10 '20 at 16:07
  • Other links regarding csrf and favicons: [stackoverflow #41787263](https://stackoverflow.com/questions/41787263/different-value-of-csrf-token-in-response-header-and-browser-cookies-csrf-verif) and [allinthehead.com article](https://allinthehead.com/retro/359/how-a-missing-favicon-broke-my-app-for-chrome-users) – verjas Jun 10 '20 at 16:11
  • Thanks again @verjas. Seems like it's easily reproducible right after Apache restart and hard page reload. – RossW Jun 10 '20 at 18:14
  • Well, you can test this solution - it worked for me: (redirect all favicon requests to a specific path): [stackoverflow #2408110 - htaccess redirect favicon](https://stackoverflow.com/questions/2408110/htaccess-redirect-favicon). Also, put a favicon in the root public folder (ex. https://example.com/favicon.ico) – verjas Jun 10 '20 at 18:38
  • Tried that - still not working. I wonder if this is related to my invisible Google Recaptcha v2 code? – RossW Jun 10 '20 at 18:41

0 Answers0