Overview
The SharePoint Server Connector retrieves document library files and list items from an on-premises SharePoint Server deployment (2013, 2016, 2019, or Subscription Edition) over its REST/OData API (and, for 2013, its XML/Atom API), and registers them in the Fess index.
This feature requires the fess-ds-sharepoint plugin.
Note
If you need to crawl SharePoint Online (Microsoft 365) instead, use Microsoft 365 Connector, not this connector. This connector’s OAuth support targets Azure ACS application-only authentication only, and it has no Microsoft Graph API integration.
Supported versions: SharePoint Server 2013 / 2016 / 2019 / Subscription Edition (SE)
Supported Content
Document library files
List items
List item attachments
Prerequisites
Plugin installation is required
The crawl account needs read access to the sites, lists, and document libraries being crawled
Choose exactly one authentication method - NTLM, Kerberos (SPNEGO), or OAuth (ACS) - and have its credentials ready
Installing the Plugin
Install it from the admin console under “System” -> “Plugin”:
Download
fess-ds-sharepoint-X.X.X.jarPlace it under
$FESS_HOME/app/WEB-INF/lib(or/usr/share/fess/app/WEB-INF/lib)Restart Fess
See Plugin for details.
Configuration
Configure this connector in the admin console under “Crawler” -> “Data Store” -> “Create New”.
Basic Settings
| Item | Example |
|---|---|
| Name | SharePoint |
| Handler Name | SharePointDataStore |
| Enabled | On |
Parameter Configuration
url=http://sharepoint.example.com/
auth.ntlm.user=DOMAIN\svc-fess
auth.ntlm.password=changeit
site.name=mysite
site.doclib_path=/Shared Documents
Parameter List
URL / Site
| Parameter | Required | Description |
|---|---|---|
url | Yes | SharePoint server base URL, e.g. http://sharepoint.example.com/ |
site.name | Conditional | Site collection name crawled under /sites/<site.name>/. Not needed if site.path is set |
site.path | No | Server-relative managed path of the site (e.g. |
site.list_id | No | Crawl a single list by GUID (List Crawl mode) |
site.list_name | No | Crawl a single list by display name (List Crawl mode) |
site.doclib_path | No | Document-library path under the site (Document Library Crawl mode), e.g. /Shared Documents |
site.exclude_list | No | Comma-separated regex patterns of list entity-type names to exclude. Only applies to a whole-site crawl |
site.exclude_folder | No | Comma-separated regex patterns of top-level folder titles to exclude. Only applies to a whole-site crawl |
site.crawl_subsites | No | Recurse into the site’s subsites (default: false). See Subsites and Managed Paths |
site.max_depth | No | How many subsite hops site.crawl_subsites may recurse (default: 10); the root is depth 0 |
Authentication
| Parameter | Required | Description |
|---|---|---|
auth.ntlm.user | No | NTLM username. Setting it enables NTLM (DOMAIN\user works) |
auth.ntlm.password | No | NTLM password |
auth.ntlm.domain | No | Windows domain, sent as its own NTLM field |
auth.ntlm.workstation | No | Workstation name sent in the NTLM negotiation |
auth.kerberos.principal | No | Client principal, written as user@REALM. Setting it enables Kerberos/SPNEGO |
auth.kerberos.keytab | No | Path to a keytab holding a key for the principal. Mutually exclusive with |
auth.kerberos.password | No | The principal’s password, used only when no keytab is set |
auth.kerberos.strip_port | No | Strip the port from the service principal name (default: true) |
auth.kerberos.use_canonical_hostname | No | Resolve the target host to its canonical name before building the service principal name (default: |
auth.kerberos.krb5_conf | No | Path to a krb5.conf. Applied only when java.security.krb5.conf is not already set |
auth.kerberos.debug | No | Enable Krb5LoginModule debug output (default: false) |
auth.oauth.client_id | No | Azure ACS application-only OAuth client ID. Setting it enables OAuth |
auth.oauth.client_secret | No | OAuth client secret |
auth.oauth.tenant | No | Tenant name, without .sharepoint.com |
auth.oauth.realm | No | Azure AD realm/directory ID |
Exactly one of auth.kerberos.principal, auth.ntlm.user, and auth.oauth.client_id may be set. See Authentication below.
List
| Parameter | Required | Description |
|---|---|---|
list.items.number_per_page | No | Page size for GetListItems (default: 100) |
list.item.content.include_fields | No | Comma-separated field names; if set, only these list-item fields are concatenated into |
list.item.content.exclude_fields | No | Comma-separated field-name patterns (each treated as a regex), excluded from |
list.is_sub_page | No | Treat list items as SitePages/wiki subpages, affecting paging fallback and the web-link shape (default: |
HTTP
| Parameter | Required | Description |
|---|---|---|
http.connection_timeout | No | HTTP connect timeout in ms; also used as the connection-pool wait timeout (default: 30000) |
http.socket_timeout | No | HTTP socket (read) timeout in ms (default: 30000) |
proxy_host | No | HTTP proxy host |
proxy_port | Conditional | HTTP proxy port; required if proxy_host is set (default: -1 = no proxy) |
Filtering & Content
| Parameter | Required | Description |
|---|---|---|
include_pattern | No | Regex an item’s value must match to be crawled. See the note under this table for what value that is |
exclude_pattern | No | Regex that excludes a matching item from being crawled |
supported_mimetypes | No | Comma-separated regexes a file’s MIME type must match at least one of (default: .*) |
max_content_length | No | Maximum file size in bytes; an over-limit file is skipped, not failed (default: |
extractor_name | No | Fallback extractor used only for a MIME type the extractor factory does not map (default: |
Behaviour
| Parameter | Required | Description |
|---|---|---|
sp.version | No | Set to |
retry_limit | No | Max retries per crawl unit on a SharePoint server/client exception (default: 2) |
role.skip | No | Skip fetching per-item permissions entirely (default: false). See Permissions |
ignore_error | No | Log and skip a file’s content-extraction failure instead of failing the crawl target (default: |
default_permissions | No | Comma-separated permission strings merged into every document’s role list in addition to whatever SharePoint returned |
delete_old_docs | No | Whether documents not refreshed this run are deleted (core default: |
number_of_threads | No | How many crawl targets are worked on at once (default: |
script_type | No | Script engine for the data-config Script (default: groovy) |
readInterval | No | Sleep between successive crawl results, in ms (default: |
Script Configuration
url=url
title=title
content=content
digest=digest
content_length=content.length()
last_modified=last_modified
role=role
Available Fields
| Key | List item (ItemCrawl) | Doclib file (FolderCrawl->FileCrawl) | Attachment (ItemAttachmentsCrawl->FileCrawl) |
|---|---|---|---|
url | Web link | File URL | File URL |
host | Hostname | Hostname | Hostname |
site | Server-relative path (FileRef) | Server-relative path | Server-relative path |
title | Title field, else FileLeafRef/filename | The doclib file’s own Title list value if present, else filename | Filename |
titleWithListName | "[listName] title" |
| "[listName] filename" |
listName | List display name, or "" | Always "" | Actual list name |
content | Concatenation of field values | Extracted text | Extracted text |
digest | Abbreviated content | Abbreviated content | Abbreviated content |
content_length | content.length() | content.length() | content.length() |
last_modified | From the listing | From the listing | From the listing |
created | From the listing | From the listing | From the listing |
mimetype | Always text/html | Detected | Detected |
filetype | Derived from mimetype | Derived from mimetype | Derived from mimetype |
role | Permission list, only if non-empty | Permission list, only if non-empty | Permission list, only if non-empty |
list_name | Present | Absent | Present |
list_id | Present | Absent | Present |
item_id | Present | Absent | Present |
Note
content_length is content.length() - the character count (UTF-16 code units) of the extracted or concatenated text, not the file’s byte size. This differs from file.size in the Box, Google Drive, and Dropbox connectors, which is the actual byte size from each service’s own file metadata. Do not compare this connector’s content_length against those.
Dynamic keys: ``val_*``
Every key of a list item’s FieldValuesAsText (the raw field-value map SharePoint returns for that item, including OData metadata keys such as odata.metadata) is exposed under two names: once unprefixed (only if that name is not already one of the fixed keys above), and once with a val_ prefix, unconditionally - e.g. a Status field becomes both Status and val_Status.
val_* keys exist only on the list-item crawl path (ItemCrawl). A document-library file (FolderCrawl->FileCrawl) or a list-item attachment (ItemAttachmentsCrawl->FileCrawl) never produces any val_* key.
Authentication
Three authentication methods are available, and exactly one may be configured. Setting more than one of auth.kerberos.principal, auth.ntlm.user, and auth.oauth.client_id fails the data config job with a validation error before any request is made. This is deliberate: only one credential is registered with the HTTP client, and the scope it is registered under matches a Negotiate challenge as readily as an NTLM one, so configuring more than one would otherwise produce 401s that nothing in the log explains.
NTLM
auth.ntlm.user={SharePoint username}
auth.ntlm.password={Password}
auth.ntlm.domain={Windows domain. Optional; unset by default.}
auth.ntlm.workstation={Workstation name sent in the NTLM negotiation. Optional; unset by default.}
auth.ntlm.domain and auth.ntlm.workstation both default to unset, which builds exactly the credential this connector has always built. Writing the domain into the username as DOMAIN\user keeps working. Setting auth.ntlm.domain sends the domain as its own NTLM field instead, which is what a server that rejects the combined form wants.
Kerberos (SPNEGO)
Supported envelope: a single crawler JVM, one krb5.conf per Fess instance, a keytab or a password, no delegation, no channel binding, and mutually exclusive with NTLM and OAuth. Anything outside that is not supported.
auth.kerberos.principal={Client principal, written as user@REALM. Setting it enables Kerberos.}
auth.kerberos.keytab={Path to a keytab holding a key for the principal. Mutually exclusive with auth.kerberos.password.}
auth.kerberos.password={The principal's password. Used only when no keytab is set.}
auth.kerberos.strip_port={true or false. Strip the port from the service principal name. Default is true.}
auth.kerberos.use_canonical_hostname={true or false. Resolve the target host to its canonical name for the service principal name. Default is false.}
auth.kerberos.krb5_conf={Path to a krb5.conf. Applied only when java.security.krb5.conf is not already set.}
auth.kerberos.debug={true or false. Krb5LoginModule debug output. Default is false.}
``krb5.conf`` belongs in ``jvm.crawler.options``, as
-Djava.security.krb5.conf=/path/to/krb5.conf. Data-store crawling runs in the crawler child process, so setting this anywhere that only affects the webapp has no effect, and a webapp restart does not pick up a change - the crawl job has to run again.auth.kerberos.krb5_confis a convenience for when nothing has set the property yet: it never overwrites an already-set value, since the property is JVM-global and one crawler JVM runs every data config in a crawl job. When it declines to overwrite, it logs a warning naming both paths.Put ``udp_preference_limit = 1`` in ``krb5.conf``’s ``[libdefaults]``. Without it, the JDK tries UDP first, and when the KDC does not answer (unreachable, a firewall dropping UDP 88, or a reply larger than the datagram size), it retries three times at thirty seconds each before falling back to TCP. A crawl that looks hung for about a minute and a half per authentication, with nothing in the log, is usually this.
Always write the principal as ``user@REALM``.
default_realmis JVM-global, and several SharePoint farms in different realms may have to share onekrb5.conf, so a bareuserresolves against whichever realm that file happens to name.``auth.kerberos.use_canonical_hostname`` defaults to ``false``, deliberately unlike Apache HttpClient’s own default. With it on, the target host is put through reverse DNS before the service principal name is built, which under alternate access mappings or behind a load balancer can produce a name no SPN is registered for - and the resulting failure says nothing about DNS. Turn it on only if the SPN really is registered against the canonical name.
IIS Extended Protection set to ``tokenChecking=Require`` cannot work. Neither Apache HttpClient 4.5 nor 5.x supports channel binding. IIS defaults this to
None, so it is usually not hit, and there is no workaround when it is.The ticket is obtained once, when the crawl’s HTTP client is built, and is never renewed. A crawl that runs longer than the ticket lifetime starts failing to authenticate partway through.
``auth.kerberos.password`` is stored and displayed in clear text, exactly like
auth.ntlm.password. Fess has no masking mechanism for data-store handler parameters; the data config edit screen renders them as a plain text area. Preferauth.kerberos.keytab, and give the keytab file restrictive permissions.auth.kerberos.debug=truemakesKrb5LoginModulewrite to the crawler process’s standard output, not to the Fess log.
OAuth (ACS)
auth.oauth.client_id={OAuth client ID}
auth.oauth.client_secret={OAuth client secret}
auth.oauth.tenant={Tenant name, without .sharepoint.com}
auth.oauth.realm={Azure AD realm/directory ID}
Setting auth.oauth.client_id enables a client-credentials (app-only) flow against the Windows Azure Access Control Service, https://accounts.accesscontrol.windows.net/{realm}/tokens/OAuth/2. The access token is fetched once, when the crawl’s HTTP client is built, applied as a Bearer Authorization header on every request, and refreshed and retried once on a 401. Microsoft has deprecated ACS and scheduled it for retirement; this connector logs a warning to that effect on every OAuth-configured crawl. There is no Entra ID app-registration (certificate or client-secret) flow implemented here - only legacy ACS app-only auth.
Only auth.oauth.client_id’s presence is checked before OAuth is wired up; client_secret, tenant, and realm are read unconditionally and can silently be blank if omitted, which breaks token acquisition with no dedicated validation message.
``sp.version=2013`` and OAuth have never worked together. Every SharePoint 2013 API call this connector makes goes through the XML/Atom client, and no code path in that client attaches an OAuth token to a request - so with both set, every request is sent unauthenticated. The crawl logs a warning saying exactly this and naming auth.ntlm.* as the alternative; it does not fail the job. Use auth.ntlm.* for SharePoint 2013.
Permissions
role.skip=true (default false) skips fetching per-item permissions entirely: no GetListItemRole call is made, no role key is ever set for the item, and the document ends up carrying only the data config’s static Permission setting and, if configured, default_permissions - no SharePoint-derived permission reaches it at all.
When roles are fetched, SharePoint’s own users, security groups, and SharePoint groups are expanded and mapped to Fess search roles:
An on-premises AD account or group (login name containing a backslash, not starting with an Azure claim prefix) is mapped via the standard AD user/group role helpers.
An Azure AD (Entra ID) account (login name starting with
i:0#.f|membership|) is mapped twice - once by its full Azure claim value, once by the AD-account portion before@in that claim - so both an Entra-ID-style and an AD-style role are added for the same user. A security group flagged as Azure (by one of several claim-style prefixes, including the specialspo-grid-all-users“everyone” group) is mapped the same way, under both forms.A SharePoint group has its own membership (users, security groups, nested groups) expanded recursively, with a visited-group guard to stop infinite recursion between groups that contain each other.
default_permissions (comma-separated) is merged in after all of the above, and applies even when SharePoint returned no role for the item at all - the case both role.skip=true and “SharePoint returned nothing” produce. The final role list is the union of the data config’s static Permission setting, the SharePoint-derived roles (unless skipped), and default_permissions, de-duplicated.
Subsites and Managed Paths
Setting site.path uses the given server-relative managed path verbatim instead of the hardcoded /sites/ prefix, and site.name is no longer required.
| Scenario | Setting |
|---|---|
| Root site collection | site.path=/ |
The /teams/eng site | site.path=/teams/eng |
The classic /sites/mysite/ form | site.name=mysite (leave site.path unset) |
Setting site.crawl_subsites (default false) makes a full site crawl - one where neither site.list_name nor site.doclib_path is set - recurse into the site’s subsites, discovered via _api/web/webinfos. Leaving it unset keeps the crawl issuing exactly the same requests it always has, including never requesting webinfos at all.
A subsite’s documents land in the same data config as the root site’s, under their own server-relative paths - there is nothing in the index that marks a document as having come from a subsite rather than the root.
site.max_depth (default 10) bounds how many subsite hops below the root site are crawled once site.crawl_subsites=true. The root site itself is depth 0, so site.max_depth=1 crawls the root’s direct children and no further. Setting it below 1 while site.crawl_subsites=true turns the feature back off - no subsite is crawled at all - and is logged as a warning when the crawl starts.
Turning subsite crawling on multiplies the crawl’s total time by roughly the number of subsites discovered (bounded by site.max_depth): each one gets its own full folder listing, list listing, and (if not at the depth bound) its own webinfos call, on top of everything the root site’s crawl already does.
number_of_threads and readInterval, described in Parallel Crawling and Load, apply to a subsite-recursive crawl the same way they apply to any other crawl.
Parallel Crawling and Load
number_of_threads (default 1) is how many crawl targets are worked on at once. At the default, the crawl runs exactly as it always has: every target is crawled on the crawling thread and no thread pool is created at all.
The value is capped at twice the processor count of the machine running Fess, so a data config cannot ask for more concurrency than the host can serve. A value below 1 - or a blank or unparseable one - falls back to 1 rather than being honoured or failing the job. A value that was capped, or one below 1, is logged with both the requested and the actual value; an unparseable one logs a warning. A blank value logs nothing, because a blank field means the parameter was simply not set.
The HTTP connection pool is sized to match. Apache HttpClient allows only 2 connections per route by default, and a whole crawl is a single route: without raising it, every thread past the second would spend the crawl waiting for a connection rather than making requests.
``readInterval`` still paces document hand-off, one document per interval, whatever it is set to. Threads make the crawl discover and fetch faster; they do not make documents reach the indexer faster. That is deliberate: dividing an operator’s configured interval by the thread count would multiply exactly the load they configured that interval to limit. A worker that finishes a document while the previous ones are still being handed over simply waits.
What raising number_of_threads does multiply is the request rate against SharePoint. The 503 backoff and the X-SharePointHealthScore wait described below are applied per crawl target, on the thread crawling it, so n threads make up to n times the requests a single-threaded crawl makes - including during a period the farm is signalling that it is busy. On an on-premises farm, raise this gradually.
Two things put a ceiling on what more threads actually buy:
The first time each SharePoint group’s membership is read, it is read by one thread at a time. Permissions are resolved through a cache shared by the whole crawl, guarded by a single lock held across a group’s member lookups. That lock stops one thread from handing another a group whose members are still being read, which would index the items that group protects with none of its permissions. Once a group is cached, every later reference to it is a cheap lookup, so this is a cold-cache cost: a crawl of a site with many distinct groups spends its early minutes closer to single-threaded than to
nthreads, while one whose items share a handful of groups barely notices.role.skip=true, which does not read permissions at all, avoids it entirely.Discovery is sequential per site: a site’s folder and list listings are one crawl target, so threads have nothing to share out until that target has finished and queued what it found.
A 503 response is retried the same as any other error, up to retry_limit, but with an increasing wait before each retry: 2 seconds, then 4, then 8, doubling up to a 30-second cap, each randomized to 70-129% of that value. A crawl target that keeps returning 503 pays this wait before every retry it actually gets, but not after its last one.
Every response - successful or not, including a page of a listing the crawl is about to discard - is inspected for the X-SharePointHealthScore response header (0 idle to 10 very busy). A score of 9 or above makes the crawl wait before doing anything else: score 9 waits about 2 seconds, score 10 about 4 seconds, and so on, doubling for each point past 9. This adds up across the whole crawl, with no aggregate cap: a farm sitting at health score 9 under sustained load adds roughly 2 seconds to every single request this connector makes - including every page of every folder and list listing - which can turn a crawl that would otherwise take hours into one that takes substantially longer. If a crawl unexpectedly slows down by an order of magnitude, check the farm’s health score during that window before assuming something else is wrong.
Configuration Examples
All of these assume NTLM. To use Kerberos or OAuth instead, see Authentication and replace the auth.ntlm.* lines.
List Crawl
Parameter:
url=http://sharepoint.example.com/
auth.ntlm.user=DOMAIN\svc-fess
auth.ntlm.password=changeit
site.name=mysite
site.list_name=Tasks
Script:
url=url
title=title
content=content
digest=digest
content_length=content.length()
last_modified=last_modified
Document Library Crawl
Parameter:
url=http://sharepoint.example.com/
auth.ntlm.user=DOMAIN\svc-fess
auth.ntlm.password=changeit
site.name=mysite
site.doclib_path=/Shared Documents
Script:
url=url
title=title
content=content
digest=digest
content_length=content.length()
last_modified=last_modified
Crawling a /teams/ Site
site.path lets you point directly at a document library on a site under a managed path other than /sites/.
Parameter:
url=http://sharepoint.example.com/
auth.ntlm.user=DOMAIN\svc-fess
auth.ntlm.password=changeit
site.path=/teams/eng
site.doclib_path=/Shared Documents
Script:
url=url
title=title
content=content
digest=digest
content_length=content.length()
last_modified=last_modified
Recursive Subsite Crawl
Starts at the root site collection and follows subsites up to 3 levels deep.
Parameter:
url=http://sharepoint.example.com/
auth.ntlm.user=DOMAIN\svc-fess
auth.ntlm.password=changeit
site.path=/
site.crawl_subsites=true
site.max_depth=3
Script:
url=url
title=title
content=content
digest=digest
content_length=content.length()
last_modified=last_modified
role=role
Limitations
No incremental or delta crawl of any kind. There is no change-token, delta-query, or “last modified since” filtering anywhere in this connector - every run does a full listing of every list, folder, and file it is configured to reach.
delete_old_docsonly controls whether documents the current full crawl did not see again are deleted afterwards; that is post-hoc cleanup, not incremental fetching.``%`` and ``#`` in file/folder names are supported on the default (non-
2013) code path. Only SharePoint Server 2019 and Subscription Edition accept those two characters in a name at all; 2016 explicitly still rejects them, and so does 2013. The default code path reaches such a file through the...ByServerRelativePath(decodedUrl=...)endpoints, which take the decoded path, and the crawl escapes both characters in the link it indexes the file under. ``sp.version=2013`` cannot reach such a file, because it uses the older...ByServerRelativeUrl(...)endpoints, which read their argument as an already-encoded URL. That is a deliberate limit rather than a gap - a SharePoint 2013 farm cannot hold such a name in the first place - so it only matters ifsp.version=2013is pointed at a 2019 or Subscription Edition server, which is not a configuration to use. See Use of # and % characters in file and folder names and File names - expanded support for special characters.IIS Extended Protection ``tokenChecking=Require`` cannot be supported. Neither Apache HttpClient 4.5 nor 5.x implements channel binding, which Extended Protection at
Requiredepends on. IIS defaults this setting toNone, so most farms are unaffected, and there is no workaround for a farm where it is set toRequire.Passwords in data-config parameters are stored and displayed in clear text. This applies to
auth.ntlm.passwordandauth.kerberos.passwordalike: Fess has no masking mechanism for data-store handler parameters, and the data config edit screen renders them in a plain text area. Preferauth.kerberos.keytaboverauth.kerberos.passwordwhere Kerberos is available, and give the keytab file restrictive permissions.``sp.version=2013`` and OAuth have never worked together. Every SharePoint 2013 API call goes through the XML/Atom client, and no code path in that client attaches an OAuth token to a request, so with both set every request is sent unauthenticated. Use
auth.ntlm.*for SharePoint 2013.Managed paths other than ``/sites/`` and the one set via ``site.path`` are still not discovered on their own.
site.crawl_subsitesrecurses only from the root site you configure, andsite.pathreaches exactly the one managed path you set, not every managed path on the farm.
Troubleshooting
Authentication Fails Silently
Symptom: requests come back 401 (or similar) with nothing clear in the log to explain why
Checklist:
Check whether more than one of
auth.kerberos.principal,auth.ntlm.user, andauth.oauth.client_idis set - two or more fails the job with a validation error before the crawl startsFor Kerberos, confirm
-Djava.security.krb5.conf=...is set injvm.crawler.options. Setting it anywhere that only affects the webapp has no effect. After changing it, re-run the crawl job - restarting the webapp does not pick it upFor Kerberos, confirm
udp_preference_limit = 1is set inkrb5.conf’s[libdefaults]. Without it, an unresponsive KDC can make each authentication hang for about 90 seconds (three 30-second UDP retries) with nothing in the logConfirm the principal is written as
user@REALM- a bareuserresolves against whateverdefault_realmthe sharedkrb5.confhappens to nameFor OAuth, confirm
client_secret,tenant, andrealmare not blank - onlyclient_id’s presence is validated, so the others can be silently emptyConfirm IIS Extended Protection is not set to
tokenChecking=Require- there is no workaround for that settingFor a long-running crawl, check whether it started failing only partway through - the Kerberos ticket is obtained once at HTTP client build time and is never renewed, so a crawl that outlives the ticket starts failing partway through
The Crawl Is Slow (503s and the Health Score)
Symptom: the crawl takes far longer than expected, or times out
Checklist:
Check the SharePoint farm’s
X-SharePointHealthScoreduring the slow window. A score of 9 or above adds a wait before every request (about 2 seconds at 9, about 4 at 10, doubling from there, with no aggregate cap), which can turn a crawl that should take hours into one that takes far longerCheck for repeated 503 responses. A 503 is retried up to
retry_limittimes, waiting 2, then 4, then 8 seconds (capped at 30) before each retryCheck whether
number_of_threadshas been raised too far. More threads mean roughly proportionally more requests against SharePoint, which can push the health score higher. Raise it gradually on an on-premises farmIf
site.crawl_subsites=true, remember that total crawl time grows roughly with the number of subsites discovered - consider narrowing the scope withsite.max_depth
Nothing Gets Indexed
Symptom: the crawl finishes normally, but search returns zero results
Checklist:
Check the crawler log for errors or warnings (set
org.codelibs.fess.dstoDEBUGinapp/WEB-INF/env/crawler/resources/log4j2.xml)Check
url,site.name(orsite.path), andsite.list_namefor typos - remember thatsite.nameis not needed oncesite.pathis setConfirm authentication is actually succeeding (no 401s) - a request that never authenticates is a far more common cause than a misconfigured
role.skipordefault_permissionsIf
include_patternorexclude_patternis set, remember these match a server-relative path (for a document-library file or a list-item attachment) or theFileRef(for a list item) - not the URL shown in search results. Check for a pattern written for a full URLCheck whether
supported_mimetypesormax_content_lengthis excluding the files you expect to seeCheck whether
site.exclude_listorsite.exclude_folderis unintentionally excluding the target
Reference Information
Data Store Connector Overview - Data Store Connector Overview
Microsoft 365 Connector - Microsoft 365 Connector (for SharePoint Online)
Data Store Crawling - Data Store Configuration Guide
Plugin - Plugin Management Guide