Artica Wiki

XML Sitemap generator

Generate and serve a sitemap.xml for any website published behind the Artica reverse-proxy by crawling its backend.

The XML Sitemap feature generates and serves a standards-compliant sitemap.xml for any website published behind the Artica reverse-proxy, without touching the backend application. 
Artica crawls the site directly on its backend servers, keeps only the canonical URLs that are really indexable, writes one or more Sitemap XML files, and serves them itself from the reverse-proxy. 
The sitemap is regenerated on a schedule, announced in robots.txt, and every decision the crawler took is visible in the console.

The feature works per published website and is disabled by default. 
It is available for reverse-proxy websites that forward to at least one backend server.

Concept

A sitemap is the list of URLs a site owner wants search engines to crawl and index. 
Applications such as WordPress, PrestaShop or Magento generate their own from their database.
Static sites, in-house applications and legacy sites usually have none, and their administrators have no simple way to produce one. 
Artica fills that gap from the one place that already sees every published site: the reverse-proxy.

How a generation works:

  1. Crawl the backend, not the public site. 
    The crawler connects to the backend servers defined for the website and sends the site's public host name in the Host header. 
    It never goes through the public listener, so it is not blocked by the Web Application Firewall, the JavaScript challenge, EdgeGuard or IP reputation rules, and it does not pollute the access logs or the statistics of the site. 
    No URL is ever sent to the crawler from the console: 
    It only receives a website identifier, so it cannot be turned into a proxy towards arbitrary hosts. 
    Redirections to any host other than the site's own host names are refused.
  2. Two independent verdicts per URL. 
    Each page receives a follow verdict (should its links be explored?) and a publish verdict (should it be listed in the sitemap?). 
    A pagination page such as /page/2 or a noindex,follow page is followed but not published: 
    This is how the crawler reaches deep articles and products without listing the pagination itself. 
    Merging the two verdicts, which is the classic mistake of this kind of tool, silently drops half of an e-commerce catalogue.
  3. Canonical URLs and deduplication. 
    URLs are normalised (host and scheme in lowercase, fragment removed, default ports removed, tracking parameters such as utm_*, gclid and fbclid removed) and rel=canonical is honoured.
     Two paths serving the same content are published once, under their canonical URL.
    The source of an internal redirection is recorded as a duplicate of its target.
  4. Classification. 
    Every published URL is classified as a page, an article or a product, from JSON-LD structured data first (Article, BlogPosting, Product…), then OpenGraph, then HTML structure. 
    Images embedded in a page are collected and attached to that page.
  5. Real modification dates. 
    The lastmod of each URL is taken from the most reliable source available, in this order: 
    JSON-LD dateModified, article:modified_time, the HTTP Last-Modified header, a <time datetime> element, the date known from the previous crawl, and finally the date of the last successful scan of that URL. 
    The generation timestamp is never used as a universal lastmod: search engines require this field to reflect an actual content change.
  6. Generation and atomic publication. 
    The inventory is turned into one file per family (pages, articles, products, images) under a sitemap.xml index. 
    Each run writes into its own directory; the files are validated, and only then is the current link switched to the new generation. 
    The previous generation keeps being served until that very instant, and stays on disk for rollback.
  7. Served by the reverse-proxy service. 
    The reverse-proxy serves /sitemap.xml and the family files itself, from local disk. 
    The backend is never asked for them. 
    When the feature is disabled, the locations are removed and the backend receives /sitemap.xml exactly as before.
  8. Announced in robots.txt. 
    When the Well-known files & robots.txt feature manages the site's robots.txt, a Sitemap: 
    line pointing to the generated sitemap is added at rendering time, deduplicated against any line you already wrote, and removed automatically when the sitemap is disabled.
  9. Scheduled. 
    A scheduler wakes up every minute, compares the end of the last successful run with the configured interval and queues the sites that are due, with a ±10 % spread so that all sites configured on the same interval do not start at the same second. 
    At most two crawls run at the same time on an appliance, whatever the number of sites.

Benefits

Use cases

Where to find it

Open Web services, select the website, and locate the XML Sitemap row in the site's parameters. 
The row shows whether the feature is active and, when it is, the number of indexable URLs found by the last successful run. 
Click the edit button to open the management window.

XML Sitemap row in the website parameters

The management window has five tabs: Overview, Settings, URLs, Exclusions and History.

Enabling the feature and settings

Open the Settings tab, switch Enable XML Sitemap on, adjust the parameters if needed and click Apply.

Settings tab

Setting Default Range Meaning
Enable XML Sitemap Off Activates the crawler, the scheduler and the reverse locations for this site.
Override Existing Sitemap Off One-shot confirmation used when the backend already serves its own /sitemap.xml, 
or when Artica could not check it (see below). It is not stored.
Generation Interval (Hours) 12 1 – 720 Time between two scheduled generations, counted from the end of the last successful run.
Maximum Pages 10 000 10 – 1 000 000 Maximum number of pages fetched in one run. When reached, the run ends with the status
 success_with_limits and the URLs that were discovered but never fetched are counted
 under the crawl_limit reason.
Maximum Crawl Depth 15 1 – 100 Maximum number of links between the home page and a page.
 Deeper URLs are counted under crawl_depth.
Concurrency 4 1 – 32 Number of simultaneous requests sent to the backend.
Request Timeout (Seconds) 15 2 – 120 Timeout of each backend request.
Maximum Page Size (Bytes) 8 388 608 262 144 – 33 554 432 Pages larger than this are not parsed and are excluded with the body_too_large reason.
Respect Robots.Txt On Reads the site's robots.txt from the backend and excludes disallowed paths
 from both the crawl and the sitemap (robots_disallow).
Include Images On Collects the images of each published page and produces an image sitemap.
Include Articles On Publishes the URLs classified as articles, in their own file.
Include Products On Publishes the URLs classified as products, in their own file.
Exclude Query Strings Off When on, any URL carrying a query string is excluded (query_string).
 Leave it off on sites that use parameters as legitimate page identifiers.

Values outside the allowed range are refused with an explicit error, never silently clamped.

What happens when you enable it

Enabling the feature triggers two checks before anything is written:

  1. The backend is probed for an existing sitemap. 
    Artica requests /sitemap.xml on the backend. 
    Three outcomes are possible:
    • the backend answers 404 or anything that is not an XML sitemap: activation proceeds;
    • the backend already serves an XML sitemap: 
      activation is refused with the message the backend already publishes its own sitemap.xml. 
      Taking over would silently hide an application sitemap that is often richer than a crawl. 
      Switch Override Existing Sitemap on and apply again to take over deliberately;
    • the backend could not be reached within 10 seconds: 
      activation is refused with a distinct message, could not verify. 
      A check that did not happen is never treated as "no sitemap found". 
      The same override option lets you proceed.
  2. The website configuration is rebuilt and validated synchronously. 
    The reverse-proxy configuration of the site is regenerated with the sitemap locations, tested, and deployed.
     If the test fails, the previous configuration stays in place. 
    Activation is also refused, without any override, when the site type does not support it, when its configuration cannot be read, or when /sitemap.xml is already served by a custom content file of the same site.

After a successful activation, the first generation starts at the next scheduler tick (within a minute). 
You can also start it immediately with Generate now in the Overview tab.

Disabling the feature removes the everse-proxy locations, so the backend receives /sitemap.xml again as before.
 The generated files stay on disk for the history but are no longer served.

Overview tab

The Overview shows the state of the feature for this site and the result of the last successful run.

Overview tab

Field Meaning
State Idle, or Running while a crawl is in progress for this site.
Last Run Start time of the last successful run. 
Failed or interrupted runs are listed in the History tab but never shown here.
Indexable URLs / Excluded URLs Number of distinct URLs of the last successful run that were published, and number that were excluded.
 Together they equal the size of the inventory shown in the URLs tab.
Pages Fetched Number of HTTP requests actually sent to the backend during the run.
Errors Number of URLs excluded because of a network error.
Next Run Nominal date of the next scheduled generation (last successful run plus the interval).
 The real start is spread by up to 10 % to avoid all sites starting together.
 The field is absent until a first successful run exists.
Published URLs Total number of entries in the published files. 
A page that carries images appears both in its family file and in the image sitemap,
 so this number can exceed the number of indexable URLs.
Top 5 exclusion reasons The most frequent reasons of the last run. See the URLs tab for the full list.

Generate now starts a manual run immediately. 
The console then polls the daemon every three seconds and refreshes the tab when the run ends. 
A second click while a run is in progress is refused (A generation is already running for this site), as is a manual run when the appliance-wide limit of two simultaneous crawls is reached.

A generation running

URLs tab

The URLs tab is the inventory built by the crawler: 
every URL discovered on the site, with its content type, HTTP status, verdict and, when excluded, the reason. 
It is paginated on the server and can be filtered by path, verdict, content type, exclusion reason and HTTP status.

URLs tab

The exclusion reasons form a closed list:

Reason The URL was excluded because…
noindex the page carries noindex in a robots or googlebot meta tag or in 
the X-Robots-Tag header. Its links are still followed unless nofollow is present.
robots_disallow the path is disallowed by the site's robots.txt (when Respect Robots.Txt is on).
non_200 the backend answered something other than 200 (404, 403, 5xx…).
redirect_external the page redirects to a host that is not one of the site's host names. 
The redirection is not followed.
canonical_external its rel=canonical points outside the site.
duplicate another URL is the canonical form of the same content: 
explicit canonical, source of an internal redirection, or identical title, H1 and visible text.
search it is an internal search results page.
pagination it is a pagination page (/page/2, ?page=2…). 
It is followed, so that deep content is discovered, but not published.
low_value it is a filter, sort, cart, checkout or similar variant with no indexing value.
unsupported_content_type the response is not HTML (PDF, JSON, image, archive…).
body_too_large the page exceeds Maximum Page Size.
soft_404 the page answers 200 but is recognised as a "not found" page.
query_string it carries a query string and Exclude Query Strings is on.
custom_exclusion it matches one of the rules of the Exclusions tab.
invalid_url the URL could not be parsed or normalised.
network_error the backend request failed (timeout, connection error).
crawl_depth it was discovered deeper than Maximum Crawl Depth and was never fetched. 
Counted in the run, but absent from the table since it has no inventory line.
crawl_limit it was discovered after Maximum Pages was reached and was never fetched. Same remark as above.

Weak heuristics never exclude a page on their own:
 an article whose slug contains the word search is not treated as a search results page. 
Detection combines the exact path, its segments, the query parameters and the HTML signals.

Exclusions tab

Exclusion rules let you keep URLs out of the sitemap even though the crawler would publish them: 
a private area, a staging section, a family of parameters. 
A rule only affects the publish verdict; the crawler still follows the links of an excluded page, so excluding a listing page does not hide what it links to.

Exclusions tab

Click Add exclusion to create a rule, or the pencil icon to edit one.

Add exclusion form

Match type Matches when… Example
exact the URL path is exactly the pattern. /private-page excludes /private-page only, not /private-pages.
prefix the URL path starts with the pattern. /prive/ excludes the whole /prive/ tree.
contains the URL path contains the pattern. /preview excludes any path containing that word.
query_param the URL carries a query parameter with that name. sessionid excludes every URL with a sessionid= parameter.

Rules are matched on the decoded path and parameters, never on the raw URL string. 
A rule can be disabled without being deleted. 
Rules take effect at the next run, scheduled or started with Generate now; 
the excluded URLs then appear in the URLs tab with the custom_exclusion reason.

History tab

The History tab lists the last runs of this site, most recent first, with their trigger (manual or scheduled), status, counters, duration, and whether the run was published.

History tab

Status Meaning Published
success The crawl completed normally. Yes
success_with_limits The crawl completed but hit Maximum Pages, or met isolated timeouts or 5xx answers.
 The sitemap is published; review the crawl_limit and network_error counters.
Yes
failed The home page did not answer, no backend was reachable, 
the generated XML did not validate, or a file-system error occurred.
No — the previous generation stays served
interrupted The run was cancelled or the daemon was stopped while it was running. No

The last two generations are kept on disk; older ones are deleted after each successful publication.

What is published

The reverse-proxy serves the following URLs for the site, from local disk, with the security modules that could block a search engine crawler disabled on these paths:

Example of the index generated for the site shown above:

<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <sitemap><loc>http://sitemap-test.articatech.unix/sitemap-pages-0001.xml</loc><lastmod>2026-09-02T13:57:45Z</lastmod></sitemap>
    <sitemap><loc>http://sitemap-test.articatech.unix/sitemap-articles-0001.xml</loc><lastmod>2026-09-02T13:57:45Z</lastmod></sitemap>
    <sitemap><loc>http://sitemap-test.articatech.unix/sitemap-products-0001.xml</loc><lastmod>2026-09-02T13:57:45Z</lastmod></sitemap>
    <sitemap><loc>http://sitemap-test.articatech.unix/sitemap-images-0001.xml</loc><lastmod>2026-09-02T13:57:45Z</lastmod></sitemap>
</sitemapindex>

A family file, with the lastmod taken from the page content:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <url><loc>http://sitemap-test.articatech.unix/</loc><lastmod>2026-08-25T08:00:00Z</lastmod></url>
    <url><loc>http://sitemap-test.articatech.unix/product-1</loc><lastmod>2026-09-02T15:52:48+02:00</lastmod></url>
</urlset>

In the image sitemap, the <loc> of each entry is the page, and its images are listed inside it, as the protocol requires:

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
    <url>
        <loc>http://sitemap-test.articatech.unix/product-1</loc>
        <image:image><image:loc>http://sitemap-test.articatech.unix/img/product-1.jpg</image:loc></image:image>
    </url>
</urlset>

When robots.txt is managed by Artica for this site, it ends with the announcement of the generated sitemap:

User-agent: *
Disallow: /prive/
Sitemap: http://sitemap-test.articatech.unix/sitemap.xml

URLs are built on the site's canonical host name and public scheme (HTTPS when the site has an SSL listener), never on the backend address.

Safety rules worth knowing

REST API

The feature is driven by the reverse-proxy daemon's REST API on the local Unix socket /run/reverse-proxy.sock. All routes are scoped by the website identifier.

Route Purpose
GET /sitemap-xml/{serviceid}/settings The twelve settings as resolved for this site.
POST /sitemap-xml/{serviceid}/settings Writes settings. Enabling probes the backend; the answer carries rebuild_ok and rebuild_error. Pass "force_replace": true (a JSON boolean) to override a conflict.
POST /sitemap-xml/{serviceid}/generate Starts a manual run and returns its run_id immediately; 409 already_running or 503 no_slots_available otherwise.
POST /sitemap-xml/{serviceid}/cancel Cooperative cancellation of the running crawl.
GET /sitemap-xml/{serviceid}/status scheduler_running (the module started on this appliance), enabled (this site), running (a crawl is in flight), last successful run, next_run_at, published manifest.
GET /sitemap-xml/{serviceid}/runs Run history, most recent first.
GET /sitemap-xml/{serviceid}/runs/{runid} One run with its exclusion counters.
GET /sitemap-xml/{serviceid}/urls Filterable, paginated inventory (q, indexable, content_type, reason, status, offset, limit up to 500).
GET / POST /sitemap-xml/{serviceid}/exclusions List, create or update exclusion rules.
POST /sitemap-xml/{serviceid}/exclusions/{id}/delete Removes a rule.

Example from the appliance shell:

curl -s --unix-socket /run/reverse-proxy.sock http://x/sitemap-xml/90/status
curl -s -X POST --unix-socket /run/reverse-proxy.sock http://x/sitemap-xml/90/generate

Troubleshooting

Symptom Cause and action
Activation refused: the backend already publishes its own sitemap.xml The application serves a sitemap. Keep it if it is good; otherwise enable Override Existing Sitemap and apply again.
Activation refused: could not verify The backend did not answer the probe within 10 seconds.
 Check the backend's health (Backend SLA, backend diagnostic) before overriding.
Activation refused: URI already served /sitemap.xml is defined as a custom content file in the site's directories. Remove that entry first.
Enabled, but no run ever appears Check scheduler_running in the status route: 
when the module could not open its database at daemon start, nothing is generated for any site. 
Restart the reverse-proxy daemon and check its log.
Runs end in failed The home page did not answer 200 on the backend, or no backend was reachable. 
Test the backend from the appliance with the site's host name in the Host header.
Zero or very few indexable URLs Look at the Top 5 exclusion reasons and filter the URLs tab by reason. 
Typical causes: a site-wide noindex on a staging backend, a robots.txt that disallows everything, every page redirecting to another host (redirect_external).
success_with_limits on every run Raise Maximum Pages or Maximum Crawl Depth, or add exclusion rules for the parameter variants that inflate the crawl.
/sitemap.xml returns 404 on the public site No generation has been published yet (first run still to come, or every run failed). 
The location exists but the file does not.