Repository navigation
Stop bots from creating a guest customer on every request - #918
Merged
Merged
Conversation
added 15 commits
October 15, 2026 12:17
…Service The decorator replaced the registered ICustomerService, which overrides a core service and broke startup for factory registrations. GET actions that write to the current customer are marked [PersistGuestCustomer] instead (SetLanguage, SetCurrency, SetTaxType); AffiliateAttribute saves the guest before it stores the affiliate. - the CustomerGuid migration makes the index unique on LiteDB too and logs an error when the Mongo index did not end up unique - a failed guest insert is retried by the next write instead of rethrown
KrzysztofPajak
force-pushed
the
feature/lazy-guest-customers
branch
from
October 15, 2026 10:19
3c9f480 to
9080293
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Type: bugfix
Issue
Every storefront request that no resolver claims ends in
WorkContextSetter.CreateCustomerGuest, which inserts aCustomerdocument and sets the guest cookie. Clients that never send the cookie back create a new document on every request. Crawler detection relied on Wangkanai.Detection, which only recognised user agents containing "bot" plus a few named crawlers, so Bytespider,...Spider,...crawlerand every scraper with a browser user agent created a guest per request. Detected crawlers caused a second problem:CustomerActivityAttributewroteLastActivityDateUtc,LastIpAddressandLastVisitedPageto the single shared SearchEngine document on each of their requests.To reproduce on
develop: request the home page several times without a cookie jar (curl -A "Mozilla/5.0 (compatible; Bytespider)" http://localhost:<port>/) and count the documents in theCustomercollection.Solution
IGuestCustomerPersistence, request scope) and gets its cookie and the Guests group. It is inserted when the browser returns the cookie (promotion inWorkContextSetter), when something writes to it (anICustomerServicedecorator registered after the business services saves it before the first write), or immediately on any other HTTP method. A cookie GUID that belongs to any existing customer is never promoted; ids are never derived from the cookie.CustomerGuid_1. Concurrent promotions of one cookie collapse into one document. The installer creates the index unique; a 2.5 migration recreates it unique on MongoDB (duplicates are logged and the index stays non-unique; the lookup index is never left missing).IUserAgentClassifier(crawler names: bot, crawl, spider, slurp, Bytespider, facebookexternalhit, meta-externalagent; mobile/tablet for the admin tab strip). Requests with anAuthorizationheader are never treated as crawlers.rel="nofollow"on the language, currency, tax type and theme selector links.Breaking changes
Wangkanai.Detectionis removed:IDetectionServiceis no longer registered andUseDetection()no longer runs. Plugins that injectedIDetectionServicemust useGrand.Web.Common.UserAgent.IUserAgentClassifier.SearchEngineCustomerResolver(addsIHttpContextAccessor, takesIUserAgentClassifier),AdminTabStripTagHelper(takesIUserAgentClassifier),ProductController(addsIGuestCustomerPersistence). Code that constructs these directly must be updated.ICustomerServiceis resolved as a decorator (TransientGuestCustomerService); a host that registersICustomerServicethrough a factory instead of a type now fails at startup with an explanatory exception.Testing
Customercollection.curl -s -o /dev/null -H "Accept: text/html" -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120" http://localhost:<port>/twice without a cookie jar: both return 200, the count does not change.curl -s -o /dev/null -A "Mozilla/5.0 (compatible; Bytespider)" http://localhost:<port>/: the count does not change and the SearchEngine document'sLastVisitedPageis unchanged.curl -c jar -b jar -H "Accept: text/html" http://localhost:<port>/twice: the count grows by one after the second call and the new document'sCustomerGuidequals the cookie value./?affiliate=<friendly name of an active affiliate>once: one new guest with thatAffiliateId./changelanguage/<seo code>?returnUrl=/: redirect to/(not/login) and one new guest with theLanguageIduser field.CustomerGuid: a new guest cookie is issued and the administrator's document is unchanged.python-requests/2.31.0: the call is authorised as the API customer.db.Customer.getIndexes()listsCustomerGuid_1withunique: true.dotnet test GrandNode.slnx: all projects pass.