Keep a record that shows who consented, when, and to what
Keep your own record of each answer, because TermsFeed Free Cookie Consent stores the choice only in the browser and does not send the answer to any server. The General Data Protection Regulation (GDPR) puts the burden of proof on you. Article 7(1) requires the controller, the business that decides why data is collected, to show that the person consented. The law leaves the format of that evidence open, so a record you send from a Cookie Consent callback to your own server is one way to hold that evidence.
A developer with access to a server endpoint sets up the record from the user_consent_saved callback in about 15 minutes.
What Cookie Consent stores, and where
Cookie Consent writes the answer into 3 first-party cookies in the browser. The cookies hold the 4 categories with true or false, the fact that the visitor answered, and a consent token. The token, 12 random characters by default, identifies the browser, not the person. Read the format and the lifetime of each cookie in the 3 cookies Cookie Consent writes.
These cookies are not evidence you hold. The visitor deletes them whenever they clear the browser, they expire after 365 days by default, and your server never sees them unless your own code reads them. The Cookie Consent script itself does not send requests. The loader at the hosted address sends a single ping to TermsFeed that carries the page hostname and no consent data.
The consent token is the link between the browser and your record. It stays the same for as long as the token cookie lasts: 365 days from the last answer by default, and 7 days in Safari. The records from that browser share it until a new token replaces it. The cookie_consent_user_consent_token cookie in the browser shows which records belong to that browser.
What to include in a consent record
A useful consent record answers who, when, what they saw, and what they chose, without storing more about the visitor than that. The GDPR principle of data minimization, in Article 5(1)(c), asks you to keep only the data the purpose needs. Store these fields for each answer.
- The consent token. Read it from
cookieconsent.cookieConsentObject.userConsentToken. - The time of the answer. Take it from your server clock when the record arrives, not from the browser.
- The choice. Store the 4 categories as
trueorfalse, as the callback receives them. - The language of the consent banner. The code, such as
enorde, comes fromcookieconsent.cookieConsentObject.i18n.userLang. - The version of the script and of your configuration. Store
4.2.0and a label for your configuration, such as the date you last changed the categories in use. - The page. Store the address where the visitor answered.
Cookie Consent shows fixed texts for each language, with no option to change them, and fixes to 4.2.0 replace the hosted file under the same version number. Keep a dated copy of the consent banner and Preferences Center texts for each language your site uses, so a record shows what the visitor read. Find the category names and the purposes each one covers in the 4 cookie categories.
How to record each answer from user_consent_saved
Send each answer to your server from the user_consent_saved callback. The callback receives the 4 categories and a reason. The reason is 'choice' when the visitor clicks the "I agree", "I decline", or "Save my preferences" button, or the "OK" button of implied mode. The reason is 'page_load' on each page load before the visitor answers, and on every later page load when user_consent_saved_on_page_load is on.
You need an endpoint on your own server that accepts a POST request with JSON. The example uses the address /consent-log. Replace it with yours.
In the complete Cookie Consent block with the callback, the if check skips page loads, so the endpoint receives answers only.
<!-- Cookie Consent by TermsFeed https://www.TermsFeed.com -->
<script type="text/javascript" src="https://www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js" charset="UTF-8"></script>
<script type="text/javascript" charset="UTF-8">
document.addEventListener('DOMContentLoaded', function () {
cookieconsent.run({
"notice_banner_type": "headline",
"consent_type": "express",
"palette": "dark",
"language": "en",
"page_load_consent_levels": ["strictly-necessary"],
"website_name": "Example Store",
"website_privacy_policy_url": "https://www.example.com/privacy-policy",
"callbacks": {
"user_consent_saved": function (details, reason) {
// Record answers only, not page loads
if (reason !== 'choice') {
return;
}
var consentObject = cookieconsent.cookieConsentObject;
var record = {
token: consentObject.userConsentToken,
choice: details,
language: consentObject.i18n.userLang,
script_version: '4.2.0',
configuration: '2026-10-18',
page: window.location.href
};
fetch('/consent-log', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(record),
keepalive: true
});
}
}
});
});
</script>
<noscript>Free cookie consent management tool by <a href="https://www.termsfeed.com/">TermsFeed Generator</a></noscript>
<!-- End Cookie Consent by TermsFeed https://www.TermsFeed.com -->
<a href="#" id="open_preferences_center">Update cookies preferences</a>
The keepalive flag asks the browser to finish the request when the page unloads.
Leave demo off while you record answers. Demo mode does not write cookies. Its answers take a token cookie saved earlier, else the user_consent_token value, else a new token on each answer. Without a saved token cookie, the demo records no longer identify one browser.
Set up the endpoint and the callback, then test the record in Chrome DevTools.
- Create the endpoint on your server, and have it store each record with the time it arrived.
- Add the
user_consent_savedcallback of the complete Cookie Consent block to your owncookieconsent.run({...})call, and keep your Cookie Consent block before the closing</body>tag. Deploy the page. - Close every private window, open the page in a new one with Chrome DevTools open, filter the Network tab for
consent-log, and click the "I agree" button. APOSTrequest appears, and its payload shows the token. In the Application tab, open Cookies and confirm thatcookie_consent_user_consent_tokenholds the same token.
Close every private window and repeat step 3 in a new one with the "I decline" button. Repeat it again with the "Change my preferences" button and the "Save my preferences" button. The page_refresh_confirmation_buttons option reloads the page right after the answer. To test the record with that option, deploy it, then repeat step 3 with "Preserve log" ticked in the Network tab before you click. The request then stays in the list after the reload. Find every callback, its arguments, and the order they run in on callbacks and DOM events.
Tie consent records to logged-in visitors
Link a consent record to a user account by storing the consent token next to the account on your server when the visitor logs in. Read cookieconsent.cookieConsentObject.userConsentToken in the browser after login and send it with the account ID to your server. In express consent the token stays undefined until the first answer, so send it from user_consent_saved when reason is 'choice', for a visitor who logs in before answering. Every record that carries that token then belongs to the account, from the first answer in that browser until the token cookie expires.
The user_consent_token option looks like a shortcut, but it writes your value, such as a user ID, into the token cookie only when the browser does not hold a token cookie. A browser that answered before keeps its earlier token, and the option is ignored, so the stored mapping of token to account stays the reliable link.
A visitor who uses 2 browsers or 2 devices gets 2 tokens and answers twice, once in each browser. Keep both tokens on the account when both reach your server.
Why a consent log does not prove that blocking works
A consent log proves what the visitor chose, not that your scripts waited for that choice. A tracking script without a tag runs before the answer whatever the log says, and the log looks the same whether the script is tagged or not. Evidence that blocking works comes from the requests the browser sends, which the log never sees.
Test the blocking after every change to your scripts or your tags, and keep the result with the date. The checks assume express consent with page_load_consent_levels left out or at the generator value ["strictly-necessary"].
- Publish your change and clear any page cache, then close every private window and open a new one with Chrome DevTools open.
- Filter the Network tab for the host of one optional tagged script, such as
connect.facebook.netfor a tagged Meta Pixel, and then load the site. - Confirm that no request appears before you click the "I agree" button. An untagged Google tag under Google Consent Mode sends
collectrequests withgcs=G100before the answer by design, so leave its host out of this test. - Save the Network tab as a HAR file as a dated record of the test. For the host of each other optional tagged script, close the window and repeat steps 2 to 4 in a new private window. Then run the full checks in verify your installation.